diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index eb77971748a335..37f58993729b8b 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -576,7 +576,6 @@ pub struct UnstableConfig {
   // TODO(bartlomieju): remove in Deno 2.5
   pub legacy_flag_enabled: bool, // --unstable
   pub bare_node_builtins: bool,
-  pub detect_cjs: bool,
   pub sloppy_imports: bool,
   pub features: Vec<String>, // --unstabe-kv --unstable-cron
 }
@@ -5720,7 +5719,6 @@ fn unstable_args_parse(
 
   flags.unstable_config.bare_node_builtins =
     matches.get_flag("unstable-bare-node-builtins");
-  flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs");
   flags.unstable_config.sloppy_imports =
     matches.get_flag("unstable-sloppy-imports");
 
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index 3aaf2bd438f363..5e5bae87da4f1a 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -7,6 +7,7 @@ mod import_map;
 mod lockfile;
 mod package_json;
 
+use deno_ast::MediaType;
 use deno_ast::SourceMapOption;
 use deno_config::deno_json::NodeModulesDirMode;
 use deno_config::workspace::CreateResolverOptions;
@@ -34,7 +35,6 @@ use import_map::resolve_import_map_value_from_specifier;
 pub use deno_config::deno_json::BenchConfig;
 pub use deno_config::deno_json::ConfigFile;
 pub use deno_config::deno_json::FmtOptionsConfig;
-pub use deno_config::deno_json::JsxImportSourceConfig;
 pub use deno_config::deno_json::LintRulesConfig;
 pub use deno_config::deno_json::ProseWrap;
 pub use deno_config::deno_json::TsConfig;
@@ -1155,21 +1155,34 @@ impl CliOptions {
     self
       .main_module_cell
       .get_or_init(|| {
-        let main_module = match &self.flags.subcommand {
+        Ok(match &self.flags.subcommand {
           DenoSubcommand::Compile(compile_flags) => {
             resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
           }
           DenoSubcommand::Eval(_) => {
-            resolve_url_or_path("./$deno$eval.ts", self.initial_cwd())?
+            resolve_url_or_path("./$deno$eval.mts", self.initial_cwd())?
           }
           DenoSubcommand::Repl(_) => {
-            resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())?
+            resolve_url_or_path("./$deno$repl.mts", self.initial_cwd())?
           }
           DenoSubcommand::Run(run_flags) => {
             if run_flags.is_stdin() {
-              resolve_url_or_path("./$deno$stdin.ts", self.initial_cwd())?
+              resolve_url_or_path("./$deno$stdin.mts", self.initial_cwd())?
             } else {
-              resolve_url_or_path(&run_flags.script, self.initial_cwd())?
+              let url =
+                resolve_url_or_path(&run_flags.script, self.initial_cwd())?;
+              if self.is_node_main()
+                && url.scheme() == "file"
+                && MediaType::from_specifier(&url) == MediaType::Unknown
+              {
+                try_resolve_node_binary_main_entrypoint(
+                  &run_flags.script,
+                  self.initial_cwd(),
+                )?
+                .unwrap_or(url)
+              } else {
+                url
+              }
             }
           }
           DenoSubcommand::Serve(run_flags) => {
@@ -1178,9 +1191,7 @@ impl CliOptions {
           _ => {
             bail!("No main module.")
           }
-        };
-
-        Ok(main_module)
+        })
       })
       .as_ref()
       .map_err(|err| deno_core::anyhow::anyhow!("{}", err))
@@ -1229,7 +1240,7 @@ impl CliOptions {
   // This is triggered via a secret environment variable which is used
   // for functionality like child_process.fork. Users should NOT depend
   // on this functionality.
-  pub fn is_npm_main(&self) -> bool {
+  pub fn is_node_main(&self) -> bool {
     NPM_PROCESS_STATE.is_some()
   }
 
@@ -1607,9 +1618,11 @@ impl CliOptions {
       || self.workspace().has_unstable("bare-node-builtins")
   }
 
-  pub fn unstable_detect_cjs(&self) -> bool {
-    self.flags.unstable_config.detect_cjs
-      || self.workspace().has_unstable("detect-cjs")
+  pub fn detect_cjs(&self) -> bool {
+    // only enabled when there's a package.json in order to not have a
+    // perf penalty for non-npm Deno projects of searching for the closest
+    // package.json beside each module
+    self.workspace().package_jsons().next().is_some() || self.is_node_main()
   }
 
   fn byonm_enabled(&self) -> bool {
@@ -1673,7 +1686,6 @@ impl CliOptions {
           "byonm",
           "bare-node-builtins",
           "fmt-component",
-          "detect-cjs",
         ])
         .collect();
 
@@ -1811,6 +1823,36 @@ fn resolve_node_modules_folder(
   Ok(Some(canonicalize_path_maybe_not_exists(&path)?))
 }
 
+fn try_resolve_node_binary_main_entrypoint(
+  specifier: &str,
+  initial_cwd: &Path,
+) -> Result<Option<Url>, AnyError> {
+  // node allows running files at paths without a `.js` extension
+  // or at directories with an index.js file
+  let path = deno_core::normalize_path(initial_cwd.join(specifier));
+  if path.is_dir() {
+    let index_file = path.join("index.js");
+    Ok(if index_file.is_file() {
+      Some(deno_path_util::url_from_file_path(&index_file)?)
+    } else {
+      None
+    })
+  } else {
+    let path = path.with_extension(
+      path
+        .extension()
+        .and_then(|s| s.to_str())
+        .map(|s| format!("{}.js", s))
+        .unwrap_or("js".to_string()),
+    );
+    if path.is_file() {
+      Ok(Some(deno_path_util::url_from_file_path(&path)?))
+    } else {
+      Ok(None)
+    }
+  }
+}
+
 fn resolve_import_map_specifier(
   maybe_import_map_path: Option<&str>,
   maybe_config_file: Option<&ConfigFile>,
diff --git a/cli/emit.rs b/cli/emit.rs
index 8c4f2091cf021f..3cd23b7abbcaa6 100644
--- a/cli/emit.rs
+++ b/cli/emit.rs
@@ -181,7 +181,6 @@ impl Emitter {
   pub async fn load_and_emit_for_hmr(
     &self,
     specifier: &ModuleSpecifier,
-    module_kind: deno_ast::ModuleKind,
   ) -> Result<String, AnyError> {
     let media_type = MediaType::from_specifier(specifier);
     let source_code = tokio::fs::read_to_string(
@@ -203,11 +202,16 @@ impl Emitter {
         // this statement is probably wrong)
         let mut options = self.transpile_and_emit_options.1.clone();
         options.source_map = SourceMapOption::None;
+        let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script(
+          specifier,
+          media_type,
+          parsed_source.compute_is_script(),
+        )?;
         let transpiled_source = parsed_source
           .transpile(
             &self.transpile_and_emit_options.0,
             &deno_ast::TranspileModuleOptions {
-              module_kind: Some(module_kind),
+              module_kind: Some(ModuleKind::from_is_cjs(is_cjs)),
             },
             &options,
           )?
diff --git a/cli/factory.rs b/cli/factory.rs
index 417f771a30c338..5cb2dd7b3aedda 100644
--- a/cli/factory.rs
+++ b/cli/factory.rs
@@ -42,12 +42,12 @@ use crate::npm::CliNpmResolverCreateOptions;
 use crate::npm::CliNpmResolverManagedSnapshotOption;
 use crate::npm::CreateInNpmPkgCheckerOptions;
 use crate::resolver::CjsTracker;
-use crate::resolver::CjsTrackerOptions;
 use crate::resolver::CliDenoResolverFs;
-use crate::resolver::CliGraphResolver;
-use crate::resolver::CliGraphResolverOptions;
 use crate::resolver::CliNodeResolver;
+use crate::resolver::CliResolver;
+use crate::resolver::CliResolverOptions;
 use crate::resolver::CliSloppyImportsResolver;
+use crate::resolver::IsCjsResolverOptions;
 use crate::resolver::NpmModuleLoader;
 use crate::resolver::SloppyImportsCachedFs;
 use crate::standalone::DenoCompileBinaryWriter;
@@ -201,7 +201,7 @@ struct CliFactoryServices {
   parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
   permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
   pkg_json_resolver: Deferred<Arc<PackageJsonResolver>>,
-  resolver: Deferred<Arc<CliGraphResolver>>,
+  resolver: Deferred<Arc<CliResolver>>,
   root_cert_store_provider: Deferred<Arc<dyn RootCertStoreProvider>>,
   root_permissions_container: Deferred<PermissionsContainer>,
   sloppy_imports_resolver: Deferred<Option<Arc<CliSloppyImportsResolver>>>,
@@ -523,14 +523,14 @@ impl CliFactory {
       .await
   }
 
-  pub async fn resolver(&self) -> Result<&Arc<CliGraphResolver>, AnyError> {
+  pub async fn resolver(&self) -> Result<&Arc<CliResolver>, AnyError> {
     self
       .services
       .resolver
       .get_or_try_init_async(
         async {
           let cli_options = self.cli_options()?;
-          Ok(Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
+          Ok(Arc::new(CliResolver::new(CliResolverOptions {
             sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
             node_resolver: Some(self.cli_node_resolver().await?.clone()),
             npm_resolver: if cli_options.no_npm() {
@@ -541,9 +541,6 @@ impl CliFactory {
             workspace_resolver: self.workspace_resolver().await?.clone(),
             bare_node_builtins_enabled: cli_options
               .unstable_bare_node_builtins(),
-            maybe_jsx_import_source_config: cli_options
-              .workspace()
-              .to_maybe_jsx_import_source_config()?,
             maybe_vendor_dir: cli_options.vendor_dir_path(),
           })))
         }
@@ -652,7 +649,6 @@ impl CliFactory {
           self.cjs_tracker()?.clone(),
           self.fs().clone(),
           Some(self.parsed_source_cache().clone()),
-          self.cli_options()?.is_npm_main(),
         );
 
         Ok(Arc::new(NodeCodeTranslator::new(
@@ -706,6 +702,7 @@ impl CliFactory {
         let cli_options = self.cli_options()?;
         Ok(Arc::new(ModuleGraphBuilder::new(
           self.caches()?.clone(),
+          self.cjs_tracker()?.clone(),
           cli_options.clone(),
           self.file_fetcher()?.clone(),
           self.fs().clone(),
@@ -794,8 +791,9 @@ impl CliFactory {
       Ok(Arc::new(CjsTracker::new(
         self.in_npm_pkg_checker()?.clone(),
         self.pkg_json_resolver().clone(),
-        CjsTrackerOptions {
-          unstable_detect_cjs: options.unstable_detect_cjs(),
+        IsCjsResolverOptions {
+          detect_cjs: options.detect_cjs(),
+          is_node_main: options.is_node_main(),
         },
       )))
     })
@@ -809,7 +807,6 @@ impl CliFactory {
       .cli_node_resolver
       .get_or_try_init_async(async {
         Ok(Arc::new(CliNodeResolver::new(
-          self.cjs_tracker()?.clone(),
           self.fs().clone(),
           self.in_npm_pkg_checker()?.clone(),
           self.node_resolver().await?.clone(),
@@ -950,10 +947,8 @@ impl CliFactory {
     let create_hmr_runner = if cli_options.has_hmr() {
       let watcher_communicator = self.watcher_communicator.clone().unwrap();
       let emitter = self.emitter()?.clone();
-      let cjs_tracker = self.cjs_tracker()?.clone();
       let fn_: crate::worker::CreateHmrRunnerCb = Box::new(move |session| {
         Box::new(HmrRunner::new(
-          cjs_tracker.clone(),
           emitter.clone(),
           session,
           watcher_communicator.clone(),
diff --git a/cli/graph_util.rs b/cli/graph_util.rs
index 46257cf785bab4..3f48449bc5d59d 100644
--- a/cli/graph_util.rs
+++ b/cli/graph_util.rs
@@ -13,16 +13,19 @@ use crate::colors;
 use crate::errors::get_error_class_name;
 use crate::file_fetcher::FileFetcher;
 use crate::npm::CliNpmResolver;
-use crate::resolver::CliGraphResolver;
+use crate::resolver::CjsTracker;
+use crate::resolver::CliResolver;
 use crate::resolver::CliSloppyImportsResolver;
 use crate::resolver::SloppyImportsCachedFs;
 use crate::tools::check;
 use crate::tools::check::TypeChecker;
 use crate::util::file_watcher::WatcherCommunicator;
 use crate::util::fs::canonicalize_path;
+use deno_config::deno_json::JsxImportSourceConfig;
 use deno_config::workspace::JsrPackageConfig;
 use deno_core::anyhow::bail;
 use deno_graph::source::LoaderChecksum;
+use deno_graph::source::ResolutionMode;
 use deno_graph::FillFromLockfileOptions;
 use deno_graph::JsrLoadError;
 use deno_graph::ModuleLoadError;
@@ -379,6 +382,7 @@ pub struct BuildFastCheckGraphOptions<'a> {
 
 pub struct ModuleGraphBuilder {
   caches: Arc<cache::Caches>,
+  cjs_tracker: Arc<CjsTracker>,
   cli_options: Arc<CliOptions>,
   file_fetcher: Arc<FileFetcher>,
   fs: Arc<dyn FileSystem>,
@@ -389,7 +393,7 @@ pub struct ModuleGraphBuilder {
   module_info_cache: Arc<ModuleInfoCache>,
   npm_resolver: Arc<dyn CliNpmResolver>,
   parsed_source_cache: Arc<ParsedSourceCache>,
-  resolver: Arc<CliGraphResolver>,
+  resolver: Arc<CliResolver>,
   root_permissions_container: PermissionsContainer,
 }
 
@@ -397,6 +401,7 @@ impl ModuleGraphBuilder {
   #[allow(clippy::too_many_arguments)]
   pub fn new(
     caches: Arc<cache::Caches>,
+    cjs_tracker: Arc<CjsTracker>,
     cli_options: Arc<CliOptions>,
     file_fetcher: Arc<FileFetcher>,
     fs: Arc<dyn FileSystem>,
@@ -407,11 +412,12 @@ impl ModuleGraphBuilder {
     module_info_cache: Arc<ModuleInfoCache>,
     npm_resolver: Arc<dyn CliNpmResolver>,
     parsed_source_cache: Arc<ParsedSourceCache>,
-    resolver: Arc<CliGraphResolver>,
+    resolver: Arc<CliResolver>,
     root_permissions_container: PermissionsContainer,
   ) -> Self {
     Self {
       caches,
+      cjs_tracker,
       cli_options,
       file_fetcher,
       fs,
@@ -518,7 +524,7 @@ impl ModuleGraphBuilder {
       None => MutLoaderRef::Owned(self.create_graph_loader()),
     };
     let cli_resolver = &self.resolver;
-    let graph_resolver = cli_resolver.as_graph_resolver();
+    let graph_resolver = self.create_graph_resolver()?;
     let graph_npm_resolver = cli_resolver.create_graph_npm_resolver();
     let maybe_file_watcher_reporter = self
       .maybe_file_watcher_reporter
@@ -543,7 +549,7 @@ impl ModuleGraphBuilder {
           npm_resolver: Some(&graph_npm_resolver),
           module_analyzer: &analyzer,
           reporter: maybe_file_watcher_reporter,
-          resolver: Some(graph_resolver),
+          resolver: Some(&graph_resolver),
           locker: locker.as_mut().map(|l| l as _),
         },
       )
@@ -666,7 +672,7 @@ impl ModuleGraphBuilder {
     };
     let parser = self.parsed_source_cache.as_capturing_parser();
     let cli_resolver = &self.resolver;
-    let graph_resolver = cli_resolver.as_graph_resolver();
+    let graph_resolver = self.create_graph_resolver()?;
     let graph_npm_resolver = cli_resolver.create_graph_npm_resolver();
 
     graph.build_fast_check_type_graph(
@@ -675,7 +681,7 @@ impl ModuleGraphBuilder {
         fast_check_cache: fast_check_cache.as_ref().map(|c| c as _),
         fast_check_dts: false,
         jsr_url_provider: &CliJsrUrlProvider,
-        resolver: Some(graph_resolver),
+        resolver: Some(&graph_resolver),
         npm_resolver: Some(&graph_npm_resolver),
         workspace_fast_check: options.workspace_fast_check,
       },
@@ -739,6 +745,18 @@ impl ModuleGraphBuilder {
       },
     )
   }
+
+  fn create_graph_resolver(&self) -> Result<CliGraphResolver, AnyError> {
+    let jsx_import_source_config = self
+      .cli_options
+      .workspace()
+      .to_maybe_jsx_import_source_config()?;
+    Ok(CliGraphResolver {
+      cjs_tracker: &self.cjs_tracker,
+      resolver: &self.resolver,
+      jsx_import_source_config,
+    })
+  }
 }
 
 /// Adds more explanatory information to a resolution error.
@@ -1143,6 +1161,53 @@ fn format_deno_graph_error(err: &dyn Error) -> String {
   message
 }
 
+#[derive(Debug)]
+struct CliGraphResolver<'a> {
+  cjs_tracker: &'a CjsTracker,
+  resolver: &'a CliResolver,
+  jsx_import_source_config: Option<JsxImportSourceConfig>,
+}
+
+impl<'a> deno_graph::source::Resolver for CliGraphResolver<'a> {
+  fn default_jsx_import_source(&self) -> Option<String> {
+    self
+      .jsx_import_source_config
+      .as_ref()
+      .and_then(|c| c.default_specifier.clone())
+  }
+
+  fn default_jsx_import_source_types(&self) -> Option<String> {
+    self
+      .jsx_import_source_config
+      .as_ref()
+      .and_then(|c| c.default_types_specifier.clone())
+  }
+
+  fn jsx_import_source_module(&self) -> &str {
+    self
+      .jsx_import_source_config
+      .as_ref()
+      .map(|c| c.module.as_str())
+      .unwrap_or(deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE)
+  }
+
+  fn resolve(
+    &self,
+    raw_specifier: &str,
+    referrer_range: &deno_graph::Range,
+    mode: ResolutionMode,
+  ) -> Result<ModuleSpecifier, ResolveError> {
+    self.resolver.resolve(
+      raw_specifier,
+      referrer_range,
+      self
+        .cjs_tracker
+        .get_referrer_kind(&referrer_range.specifier),
+      mode,
+    )
+  }
+}
+
 #[cfg(test)]
 mod test {
   use std::sync::Arc;
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index 683a59c2199cb3..9f26de70cb6d84 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -39,6 +39,7 @@ use deno_semver::package::PackageReq;
 use deno_semver::package::PackageReqReference;
 use deno_semver::Version;
 use import_map::ImportMap;
+use node_resolver::NodeModuleKind;
 use once_cell::sync::Lazy;
 use regex::Regex;
 use std::borrow::Cow;
@@ -467,6 +468,7 @@ impl<'a> TsResponseImportMapper<'a> {
     &self,
     specifier: &str,
     referrer: &ModuleSpecifier,
+    referrer_kind: NodeModuleKind,
   ) -> Option<String> {
     let specifier_stem = specifier.strip_suffix(".js").unwrap_or(specifier);
     let specifiers = std::iter::once(Cow::Borrowed(specifier)).chain(
@@ -477,7 +479,7 @@ impl<'a> TsResponseImportMapper<'a> {
     for specifier in specifiers {
       if let Some(specifier) = self
         .resolver
-        .as_graph_resolver(Some(&self.file_referrer))
+        .as_cli_resolver(Some(&self.file_referrer))
         .resolve(
           &specifier,
           &deno_graph::Range {
@@ -485,6 +487,7 @@ impl<'a> TsResponseImportMapper<'a> {
             start: deno_graph::Position::zeroed(),
             end: deno_graph::Position::zeroed(),
           },
+          referrer_kind,
           ResolutionMode::Types,
         )
         .ok()
@@ -507,10 +510,11 @@ impl<'a> TsResponseImportMapper<'a> {
     &self,
     specifier_text: &str,
     referrer: &ModuleSpecifier,
+    referrer_kind: NodeModuleKind,
   ) -> bool {
     self
       .resolver
-      .as_graph_resolver(Some(&self.file_referrer))
+      .as_cli_resolver(Some(&self.file_referrer))
       .resolve(
         specifier_text,
         &deno_graph::Range {
@@ -518,6 +522,7 @@ impl<'a> TsResponseImportMapper<'a> {
           start: deno_graph::Position::zeroed(),
           end: deno_graph::Position::zeroed(),
         },
+        referrer_kind,
         deno_graph::source::ResolutionMode::Types,
       )
       .is_ok()
@@ -586,6 +591,7 @@ fn try_reverse_map_package_json_exports(
 /// like an import and rewrite the import specifier to include the extension
 pub fn fix_ts_import_changes(
   referrer: &ModuleSpecifier,
+  referrer_kind: NodeModuleKind,
   changes: &[tsc::FileTextChanges],
   language_server: &language_server::Inner,
 ) -> Result<Vec<tsc::FileTextChanges>, AnyError> {
@@ -602,8 +608,8 @@ pub fn fix_ts_import_changes(
           if let Some(captures) = IMPORT_SPECIFIER_RE.captures(line) {
             let specifier =
               captures.iter().skip(1).find_map(|s| s).unwrap().as_str();
-            if let Some(new_specifier) =
-              import_mapper.check_unresolved_specifier(specifier, referrer)
+            if let Some(new_specifier) = import_mapper
+              .check_unresolved_specifier(specifier, referrer, referrer_kind)
             {
               line.replace(specifier, &new_specifier)
             } else {
@@ -633,6 +639,7 @@ pub fn fix_ts_import_changes(
 /// resolution by Deno (includes the extension).
 fn fix_ts_import_action<'a>(
   referrer: &ModuleSpecifier,
+  referrer_kind: NodeModuleKind,
   action: &'a tsc::CodeFixAction,
   language_server: &language_server::Inner,
 ) -> Option<Cow<'a, tsc::CodeFixAction>> {
@@ -652,7 +659,7 @@ fn fix_ts_import_action<'a>(
   };
   let import_mapper = language_server.get_ts_response_import_mapper(referrer);
   if let Some(new_specifier) =
-    import_mapper.check_unresolved_specifier(specifier, referrer)
+    import_mapper.check_unresolved_specifier(specifier, referrer, referrer_kind)
   {
     let description = action.description.replace(specifier, &new_specifier);
     let changes = action
@@ -683,7 +690,7 @@ fn fix_ts_import_action<'a>(
       fix_id: None,
       fix_all_description: None,
     }))
-  } else if !import_mapper.is_valid_import(specifier, referrer) {
+  } else if !import_mapper.is_valid_import(specifier, referrer, referrer_kind) {
     None
   } else {
     Some(Cow::Borrowed(action))
@@ -1017,6 +1024,7 @@ impl CodeActionCollection {
   pub fn add_ts_fix_action(
     &mut self,
     specifier: &ModuleSpecifier,
+    specifier_kind: NodeModuleKind,
     action: &tsc::CodeFixAction,
     diagnostic: &lsp::Diagnostic,
     language_server: &language_server::Inner,
@@ -1034,7 +1042,8 @@ impl CodeActionCollection {
         "The action returned from TypeScript is unsupported.",
       ));
     }
-    let Some(action) = fix_ts_import_action(specifier, action, language_server)
+    let Some(action) =
+      fix_ts_import_action(specifier, specifier_kind, action, language_server)
     else {
       return Ok(());
     };
@@ -1276,6 +1285,9 @@ impl CodeActionCollection {
         import_start_from_specifier(document, i)
       })?;
       let referrer = document.specifier();
+      let referrer_kind = language_server
+        .is_cjs_resolver
+        .get_doc_module_kind(document);
       let file_referrer = document.file_referrer();
       let config_data = language_server
         .config
@@ -1298,10 +1310,11 @@ impl CodeActionCollection {
         if !config_data.byonm {
           return None;
         }
-        if !language_server
-          .resolver
-          .is_bare_package_json_dep(&dep_key, referrer)
-        {
+        if !language_server.resolver.is_bare_package_json_dep(
+          &dep_key,
+          referrer,
+          referrer_kind,
+        ) {
           return None;
         }
         NpmPackageReqReference::from_str(&format!("npm:{}", &dep_key)).ok()?
@@ -1320,7 +1333,7 @@ impl CodeActionCollection {
       }
       if language_server
         .resolver
-        .npm_to_file_url(&npm_ref, document.specifier(), file_referrer)
+        .npm_to_file_url(&npm_ref, referrer, referrer_kind, file_referrer)
         .is_some()
       {
         // The package import has types.
diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs
index 1590743b2b8f84..3ee8ae93e410f6 100644
--- a/cli/lsp/completions.rs
+++ b/cli/lsp/completions.rs
@@ -9,6 +9,7 @@ use super::jsr::CliJsrSearchApi;
 use super::lsp_custom;
 use super::npm::CliNpmSearchApi;
 use super::registries::ModuleRegistry;
+use super::resolver::LspIsCjsResolver;
 use super::resolver::LspResolver;
 use super::search::PackageSearchApi;
 use super::tsc;
@@ -35,6 +36,7 @@ use deno_semver::package::PackageNv;
 use import_map::ImportMap;
 use indexmap::IndexSet;
 use lsp_types::CompletionList;
+use node_resolver::NodeModuleKind;
 use once_cell::sync::Lazy;
 use regex::Regex;
 use tower_lsp::lsp_types as lsp;
@@ -159,15 +161,17 @@ pub async fn get_import_completions(
   jsr_search_api: &CliJsrSearchApi,
   npm_search_api: &CliNpmSearchApi,
   documents: &Documents,
+  is_cjs_resolver: &LspIsCjsResolver,
   resolver: &LspResolver,
   maybe_import_map: Option<&ImportMap>,
 ) -> Option<lsp::CompletionResponse> {
   let document = documents.get(specifier)?;
+  let specifier_kind = is_cjs_resolver.get_doc_module_kind(&document);
   let file_referrer = document.file_referrer();
   let (text, _, range) = document.get_maybe_dependency(position)?;
   let range = to_narrow_lsp_range(document.text_info(), &range);
   let resolved = resolver
-    .as_graph_resolver(file_referrer)
+    .as_cli_resolver(file_referrer)
     .resolve(
       &text,
       &Range {
@@ -175,6 +179,7 @@ pub async fn get_import_completions(
         start: deno_graph::Position::zeroed(),
         end: deno_graph::Position::zeroed(),
       },
+      specifier_kind,
       ResolutionMode::Execution,
     )
     .ok();
@@ -201,7 +206,7 @@ pub async fn get_import_completions(
     // completions for import map specifiers
     Some(lsp::CompletionResponse::List(completion_list))
   } else if let Some(completion_list) =
-    get_local_completions(specifier, &text, &range, resolver)
+    get_local_completions(specifier, specifier_kind, &text, &range, resolver)
   {
     // completions for local relative modules
     Some(lsp::CompletionResponse::List(completion_list))
@@ -355,24 +360,26 @@ fn get_import_map_completions(
 
 /// Return local completions that are relative to the base specifier.
 fn get_local_completions(
-  base: &ModuleSpecifier,
+  referrer: &ModuleSpecifier,
+  referrer_kind: NodeModuleKind,
   text: &str,
   range: &lsp::Range,
   resolver: &LspResolver,
 ) -> Option<CompletionList> {
-  if base.scheme() != "file" {
+  if referrer.scheme() != "file" {
     return None;
   }
   let parent = &text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
   let resolved_parent = resolver
-    .as_graph_resolver(Some(base))
+    .as_cli_resolver(Some(referrer))
     .resolve(
       parent,
       &Range {
-        specifier: base.clone(),
+        specifier: referrer.clone(),
         start: deno_graph::Position::zeroed(),
         end: deno_graph::Position::zeroed(),
       },
+      referrer_kind,
       ResolutionMode::Execution,
     )
     .ok()?;
@@ -385,7 +392,7 @@ fn get_local_completions(
         let de = de.ok()?;
         let label = de.path().file_name()?.to_string_lossy().to_string();
         let entry_specifier = resolve_path(de.path().to_str()?, &cwd).ok()?;
-        if entry_specifier == *base {
+        if entry_specifier == *referrer {
           return None;
         }
         let full_text = format!("{parent}{label}");
@@ -905,6 +912,7 @@ mod tests {
       ModuleSpecifier::from_file_path(file_c).expect("could not create");
     let actual = get_local_completions(
       &specifier,
+      NodeModuleKind::Esm,
       "./",
       &lsp::Range {
         start: lsp::Position {
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index 34bf64446d0259..ea77e36bcfe1c1 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -4,6 +4,7 @@ use deno_ast::MediaType;
 use deno_config::deno_json::DenoJsonCache;
 use deno_config::deno_json::FmtConfig;
 use deno_config::deno_json::FmtOptionsConfig;
+use deno_config::deno_json::JsxImportSourceConfig;
 use deno_config::deno_json::LintConfig;
 use deno_config::deno_json::NodeModulesDirMode;
 use deno_config::deno_json::TestConfig;
@@ -1654,6 +1655,17 @@ impl ConfigData {
     self.member_dir.maybe_pkg_json()
   }
 
+  pub fn maybe_jsx_import_source_config(
+    &self,
+  ) -> Option<JsxImportSourceConfig> {
+    self
+      .member_dir
+      .workspace
+      .to_maybe_jsx_import_source_config()
+      .ok()
+      .flatten()
+  }
+
   pub fn scope_contains_specifier(&self, specifier: &ModuleSpecifier) -> bool {
     specifier.as_str().starts_with(self.scope.as_str())
       || self
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index 83c00d27edadea..e4fb82e58d5122 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -1707,6 +1707,7 @@ mod tests {
         documents: Arc::new(documents),
         assets: Default::default(),
         config: Arc::new(config),
+        is_cjs_resolver: Default::default(),
         resolver,
       },
     )
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index ce13c32157b119..b62fa85533856a 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -3,7 +3,9 @@
 use super::cache::calculate_fs_version;
 use super::cache::LspCache;
 use super::config::Config;
+use super::resolver::LspIsCjsResolver;
 use super::resolver::LspResolver;
+use super::resolver::SingleReferrerGraphResolver;
 use super::testing::TestCollector;
 use super::testing::TestModule;
 use super::text::LineIndex;
@@ -33,6 +35,7 @@ use deno_semver::npm::NpmPackageReqReference;
 use deno_semver::package::PackageReq;
 use indexmap::IndexMap;
 use indexmap::IndexSet;
+use node_resolver::NodeModuleKind;
 use std::borrow::Cow;
 use std::collections::BTreeMap;
 use std::collections::BTreeSet;
@@ -293,6 +296,8 @@ pub struct Document {
   /// Contains the last-known-good set of dependencies from parsing the module.
   config: Arc<Config>,
   dependencies: Arc<IndexMap<String, deno_graph::Dependency>>,
+  /// If this is maybe a CJS script and maybe not an ES module.
+  is_script: Option<bool>,
   // TODO(nayeemrmn): This is unused, use it for scope attribution for remote
   // modules.
   file_referrer: Option<ModuleSpecifier>,
@@ -323,6 +328,7 @@ impl Document {
     maybe_lsp_version: Option<i32>,
     maybe_language_id: Option<LanguageId>,
     maybe_headers: Option<HashMap<String, String>>,
+    is_cjs_resolver: &LspIsCjsResolver,
     resolver: Arc<LspResolver>,
     config: Arc<Config>,
     cache: &Arc<LspCache>,
@@ -342,6 +348,7 @@ impl Document {
           maybe_headers.as_ref(),
           media_type,
           file_referrer.as_ref(),
+          is_cjs_resolver,
           &resolver,
         )
       } else {
@@ -367,6 +374,7 @@ impl Document {
         file_referrer.as_ref(),
       ),
       file_referrer,
+      is_script: maybe_module.as_ref().map(|m| m.is_script),
       maybe_types_dependency,
       line_index,
       maybe_language_id,
@@ -388,6 +396,7 @@ impl Document {
 
   fn with_new_config(
     &self,
+    is_cjs_resolver: &LspIsCjsResolver,
     resolver: Arc<LspResolver>,
     config: Arc<Config>,
   ) -> Arc<Self> {
@@ -399,6 +408,7 @@ impl Document {
     let dependencies;
     let maybe_types_dependency;
     let maybe_parsed_source;
+    let is_script;
     let maybe_test_module_fut;
     if media_type != self.media_type {
       let parsed_source_result =
@@ -408,6 +418,7 @@ impl Document {
         &parsed_source_result,
         self.maybe_headers.as_ref(),
         self.file_referrer.as_ref(),
+        is_cjs_resolver,
         &resolver,
       )
       .ok();
@@ -415,6 +426,7 @@ impl Document {
         .as_ref()
         .map(|m| Arc::new(m.dependencies.clone()))
         .unwrap_or_default();
+      is_script = maybe_module.as_ref().map(|m| m.is_script);
       maybe_types_dependency = maybe_module
         .as_ref()
         .and_then(|m| Some(Arc::new(m.maybe_types_dependency.clone()?)));
@@ -422,10 +434,19 @@ impl Document {
       maybe_test_module_fut =
         get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &config);
     } else {
-      let graph_resolver =
-        resolver.as_graph_resolver(self.file_referrer.as_ref());
+      let cli_resolver = resolver.as_cli_resolver(self.file_referrer.as_ref());
       let npm_resolver =
         resolver.create_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());
+      let resolver = SingleReferrerGraphResolver {
+        valid_referrer: &self.specifier,
+        referrer_kind: is_cjs_resolver
+          .get_lsp_referrer_kind(&self.specifier, self.is_script),
+        cli_resolver,
+        jsx_import_source_config: jsx_import_source_config.as_ref(),
+      };
       dependencies = Arc::new(
         self
           .dependencies
@@ -436,7 +457,7 @@ impl Document {
               d.with_new_resolver(
                 s,
                 &CliJsrUrlProvider,
-                Some(graph_resolver),
+                Some(&resolver),
                 Some(&npm_resolver),
               ),
             )
@@ -446,10 +467,11 @@ impl Document {
       maybe_types_dependency = self.maybe_types_dependency.as_ref().map(|d| {
         Arc::new(d.with_new_resolver(
           &CliJsrUrlProvider,
-          Some(graph_resolver),
+          Some(&resolver),
           Some(&npm_resolver),
         ))
       });
+      is_script = self.is_script;
       maybe_parsed_source = self.maybe_parsed_source().cloned();
       maybe_test_module_fut = self
         .maybe_test_module_fut
@@ -461,6 +483,7 @@ impl Document {
       // updated properties
       dependencies,
       file_referrer: self.file_referrer.clone(),
+      is_script,
       maybe_types_dependency,
       maybe_navigation_tree: Mutex::new(None),
       // maintain - this should all be copies/clones
@@ -485,6 +508,7 @@ impl Document {
 
   fn with_change(
     &self,
+    is_cjs_resolver: &LspIsCjsResolver,
     version: i32,
     changes: Vec<lsp::TextDocumentContentChangeEvent>,
   ) -> Result<Arc<Self>, AnyError> {
@@ -518,6 +542,7 @@ impl Document {
         self.maybe_headers.as_ref(),
         media_type,
         self.file_referrer.as_ref(),
+        is_cjs_resolver,
         self.resolver.as_ref(),
       )
     } else {
@@ -541,6 +566,7 @@ impl Document {
       get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &self.config);
     Ok(Arc::new(Self {
       config: self.config.clone(),
+      is_script: maybe_module.as_ref().map(|m| m.is_script),
       specifier: self.specifier.clone(),
       file_referrer: self.file_referrer.clone(),
       maybe_fs_version: self.maybe_fs_version.clone(),
@@ -575,6 +601,7 @@ impl Document {
       ),
       maybe_language_id: self.maybe_language_id,
       dependencies: self.dependencies.clone(),
+      is_script: self.is_script,
       maybe_types_dependency: self.maybe_types_dependency.clone(),
       text: self.text.clone(),
       text_info_cell: once_cell::sync::OnceCell::new(),
@@ -602,6 +629,7 @@ impl Document {
       ),
       maybe_language_id: self.maybe_language_id,
       dependencies: self.dependencies.clone(),
+      is_script: self.is_script,
       maybe_types_dependency: self.maybe_types_dependency.clone(),
       text: self.text.clone(),
       text_info_cell: once_cell::sync::OnceCell::new(),
@@ -650,6 +678,13 @@ impl Document {
       })
   }
 
+  /// If this is maybe a CJS script and maybe not an ES module.
+  ///
+  /// Use `LspIsCjsResolver` to determine for sure.
+  pub fn is_script(&self) -> Option<bool> {
+    self.is_script
+  }
+
   pub fn line_index(&self) -> Arc<LineIndex> {
     self.line_index.clone()
   }
@@ -797,6 +832,7 @@ impl FileSystemDocuments {
   pub fn get(
     &self,
     specifier: &ModuleSpecifier,
+    is_cjs_resolver: &LspIsCjsResolver,
     resolver: &Arc<LspResolver>,
     config: &Arc<Config>,
     cache: &Arc<LspCache>,
@@ -820,7 +856,14 @@ impl FileSystemDocuments {
     };
     if dirty {
       // attempt to update the file on the file system
-      self.refresh_document(specifier, resolver, config, cache, file_referrer)
+      self.refresh_document(
+        specifier,
+        is_cjs_resolver,
+        resolver,
+        config,
+        cache,
+        file_referrer,
+      )
     } else {
       old_doc
     }
@@ -831,6 +874,7 @@ impl FileSystemDocuments {
   fn refresh_document(
     &self,
     specifier: &ModuleSpecifier,
+    is_cjs_resolver: &LspIsCjsResolver,
     resolver: &Arc<LspResolver>,
     config: &Arc<Config>,
     cache: &Arc<LspCache>,
@@ -847,6 +891,7 @@ impl FileSystemDocuments {
         None,
         None,
         None,
+        is_cjs_resolver,
         resolver.clone(),
         config.clone(),
         cache,
@@ -863,6 +908,7 @@ impl FileSystemDocuments {
         None,
         None,
         None,
+        is_cjs_resolver,
         resolver.clone(),
         config.clone(),
         cache,
@@ -890,6 +936,7 @@ impl FileSystemDocuments {
         None,
         None,
         maybe_headers,
+        is_cjs_resolver,
         resolver.clone(),
         config.clone(),
         cache,
@@ -930,6 +977,11 @@ pub struct Documents {
   /// The DENO_DIR that the documents looks for non-file based modules.
   cache: Arc<LspCache>,
   config: Arc<Config>,
+  /// Resolver for detecting if a document is CJS or ESM.
+  is_cjs_resolver: Arc<LspIsCjsResolver>,
+  /// A resolver that takes into account currently loaded import map and JSX
+  /// settings.
+  resolver: Arc<LspResolver>,
   /// A flag that indicates that stated data is potentially invalid and needs to
   /// be recalculated before being considered valid.
   dirty: bool,
@@ -937,9 +989,6 @@ pub struct Documents {
   open_docs: HashMap<ModuleSpecifier, Arc<Document>>,
   /// Documents stored on the file system.
   file_system_docs: Arc<FileSystemDocuments>,
-  /// A resolver that takes into account currently loaded import map and JSX
-  /// settings.
-  resolver: Arc<LspResolver>,
   /// The npm package requirements found in npm specifiers.
   npm_reqs_by_scope:
     Arc<BTreeMap<Option<ModuleSpecifier>, BTreeSet<PackageReq>>>,
@@ -970,6 +1019,7 @@ impl Documents {
       // the cache for remote modules here in order to get the
       // x-typescript-types?
       None,
+      &self.is_cjs_resolver,
       self.resolver.clone(),
       self.config.clone(),
       &self.cache,
@@ -1004,7 +1054,7 @@ impl Documents {
         ))
       })?;
     self.dirty = true;
-    let doc = doc.with_change(version, changes)?;
+    let doc = doc.with_change(&self.is_cjs_resolver, version, changes)?;
     self.open_docs.insert(doc.specifier().clone(), doc.clone());
     Ok(doc)
   }
@@ -1133,6 +1183,7 @@ impl Documents {
       if let Some(old_doc) = old_doc {
         self.file_system_docs.get(
           specifier,
+          &self.is_cjs_resolver,
           &self.resolver,
           &self.config,
           &self.cache,
@@ -1157,6 +1208,7 @@ impl Documents {
     } else {
       self.file_system_docs.get(
         &specifier,
+        &self.is_cjs_resolver,
         &self.resolver,
         &self.config,
         &self.cache,
@@ -1215,12 +1267,15 @@ impl Documents {
     referrer: &ModuleSpecifier,
     file_referrer: Option<&ModuleSpecifier>,
   ) -> Vec<Option<(ModuleSpecifier, MediaType)>> {
-    let document = self.get(referrer);
-    let file_referrer = document
+    let referrer_doc = self.get(referrer);
+    let file_referrer = referrer_doc
       .as_ref()
       .and_then(|d| d.file_referrer())
       .or(file_referrer);
-    let dependencies = document.as_ref().map(|d| d.dependencies());
+    let dependencies = referrer_doc.as_ref().map(|d| d.dependencies());
+    let referrer_kind = self
+      .is_cjs_resolver
+      .get_maybe_doc_module_kind(referrer, referrer_doc.as_deref());
     let mut results = Vec::new();
     for raw_specifier in raw_specifiers {
       if raw_specifier.starts_with("asset:") {
@@ -1237,31 +1292,35 @@ impl Documents {
           results.push(self.resolve_dependency(
             specifier,
             referrer,
+            referrer_kind,
             file_referrer,
           ));
         } else if let Some(specifier) = dep.maybe_code.maybe_specifier() {
           results.push(self.resolve_dependency(
             specifier,
             referrer,
+            referrer_kind,
             file_referrer,
           ));
         } else {
           results.push(None);
         }
       } else if let Ok(specifier) =
-        self.resolver.as_graph_resolver(file_referrer).resolve(
+        self.resolver.as_cli_resolver(file_referrer).resolve(
           raw_specifier,
           &deno_graph::Range {
             specifier: referrer.clone(),
             start: deno_graph::Position::zeroed(),
             end: deno_graph::Position::zeroed(),
           },
+          referrer_kind,
           ResolutionMode::Types,
         )
       {
         results.push(self.resolve_dependency(
           &specifier,
           referrer,
+          referrer_kind,
           file_referrer,
         ));
       } else {
@@ -1280,7 +1339,11 @@ impl Documents {
   ) {
     self.config = Arc::new(config.clone());
     self.cache = Arc::new(cache.clone());
+    self.is_cjs_resolver = Arc::new(LspIsCjsResolver::new(cache));
     self.resolver = resolver.clone();
+
+    node_resolver::PackageJsonThreadLocalCache::clear();
+
     {
       let fs_docs = &self.file_system_docs;
       // Clean up non-existent documents.
@@ -1300,14 +1363,21 @@ impl Documents {
         if !config.specifier_enabled(doc.specifier()) {
           continue;
         }
-        *doc = doc.with_new_config(self.resolver.clone(), self.config.clone());
+        *doc = doc.with_new_config(
+          &self.is_cjs_resolver,
+          self.resolver.clone(),
+          self.config.clone(),
+        );
       }
       for mut doc in self.file_system_docs.docs.iter_mut() {
         if !config.specifier_enabled(doc.specifier()) {
           continue;
         }
-        *doc.value_mut() =
-          doc.with_new_config(self.resolver.clone(), self.config.clone());
+        *doc.value_mut() = doc.with_new_config(
+          &self.is_cjs_resolver,
+          self.resolver.clone(),
+          self.config.clone(),
+        );
       }
       self.open_docs = open_docs;
       let mut preload_count = 0;
@@ -1324,6 +1394,7 @@ impl Documents {
         {
           fs_docs.refresh_document(
             specifier,
+            &self.is_cjs_resolver,
             &self.resolver,
             &self.config,
             &self.cache,
@@ -1409,6 +1480,7 @@ impl Documents {
     &self,
     specifier: &ModuleSpecifier,
     referrer: &ModuleSpecifier,
+    referrer_kind: NodeModuleKind,
     file_referrer: Option<&ModuleSpecifier>,
   ) -> Option<(ModuleSpecifier, MediaType)> {
     if let Some(module_name) = specifier.as_str().strip_prefix("node:") {
@@ -1422,10 +1494,12 @@ impl Documents {
     let mut specifier = specifier.clone();
     let mut media_type = None;
     if let Ok(npm_ref) = NpmPackageReqReference::from_specifier(&specifier) {
-      let (s, mt) =
-        self
-          .resolver
-          .npm_to_file_url(&npm_ref, referrer, file_referrer)?;
+      let (s, mt) = self.resolver.npm_to_file_url(
+        &npm_ref,
+        referrer,
+        referrer_kind,
+        file_referrer,
+      )?;
       specifier = s;
       media_type = Some(mt);
     }
@@ -1435,7 +1509,8 @@ impl Documents {
       return Some((specifier, media_type));
     };
     if let Some(types) = doc.maybe_types_dependency().maybe_specifier() {
-      self.resolve_dependency(types, &specifier, file_referrer)
+      let specifier_kind = self.is_cjs_resolver.get_doc_module_kind(&doc);
+      self.resolve_dependency(types, &specifier, specifier_kind, file_referrer)
     } else {
       Some((doc.specifier().clone(), doc.media_type()))
     }
@@ -1503,6 +1578,7 @@ fn parse_and_analyze_module(
   maybe_headers: Option<&HashMap<String, String>>,
   media_type: MediaType,
   file_referrer: Option<&ModuleSpecifier>,
+  is_cjs_resolver: &LspIsCjsResolver,
   resolver: &LspResolver,
 ) -> (Option<ParsedSourceResult>, Option<ModuleResult>) {
   let parsed_source_result = parse_source(specifier.clone(), text, media_type);
@@ -1511,6 +1587,7 @@ fn parse_and_analyze_module(
     &parsed_source_result,
     maybe_headers,
     file_referrer,
+    is_cjs_resolver,
     resolver,
   );
   (Some(parsed_source_result), Some(module_result))
@@ -1536,11 +1613,26 @@ fn analyze_module(
   parsed_source_result: &ParsedSourceResult,
   maybe_headers: Option<&HashMap<String, String>>,
   file_referrer: Option<&ModuleSpecifier>,
+  is_cjs_resolver: &LspIsCjsResolver,
   resolver: &LspResolver,
 ) -> ModuleResult {
   match parsed_source_result {
     Ok(parsed_source) => {
       let npm_resolver = resolver.create_graph_npm_resolver(file_referrer);
+      let cli_resolver = resolver.as_cli_resolver(file_referrer);
+      let config_data = resolver.as_config_data(file_referrer);
+      let valid_referrer = specifier.clone();
+      let jsx_import_source_config =
+        config_data.and_then(|d| d.maybe_jsx_import_source_config());
+      let resolver = SingleReferrerGraphResolver {
+        valid_referrer: &valid_referrer,
+        referrer_kind: is_cjs_resolver.get_lsp_referrer_kind(
+          &specifier,
+          Some(parsed_source.compute_is_script()),
+        ),
+        cli_resolver,
+        jsx_import_source_config: jsx_import_source_config.as_ref(),
+      };
       Ok(deno_graph::parse_module_from_ast(
         deno_graph::ParseModuleFromAstOptions {
           graph_kind: deno_graph::GraphKind::TypesOnly,
@@ -1551,7 +1643,7 @@ fn analyze_module(
           // dynamic imports like import(`./dir/${something}`) in the LSP
           file_system: &deno_graph::source::NullFileSystem,
           jsr_url_provider: &CliJsrUrlProvider,
-          maybe_resolver: Some(resolver.as_graph_resolver(file_referrer)),
+          maybe_resolver: Some(&resolver),
           maybe_npm_resolver: Some(&npm_resolver),
         },
       ))
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 2554fa34b1853f..b2bd72416ac8c7 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -22,6 +22,7 @@ use deno_semver::jsr::JsrPackageReqReference;
 use indexmap::Equivalent;
 use indexmap::IndexSet;
 use log::error;
+use node_resolver::NodeModuleKind;
 use serde::Deserialize;
 use serde_json::from_value;
 use std::collections::BTreeMap;
@@ -77,6 +78,7 @@ use super::parent_process_checker;
 use super::performance::Performance;
 use super::refactor;
 use super::registries::ModuleRegistry;
+use super::resolver::LspIsCjsResolver;
 use super::resolver::LspResolver;
 use super::testing;
 use super::text;
@@ -144,6 +146,7 @@ pub struct StateSnapshot {
   pub project_version: usize,
   pub assets: AssetsSnapshot,
   pub config: Arc<Config>,
+  pub is_cjs_resolver: Arc<LspIsCjsResolver>,
   pub documents: Arc<Documents>,
   pub resolver: Arc<LspResolver>,
 }
@@ -203,6 +206,7 @@ pub struct Inner {
   pub documents: Documents,
   http_client_provider: Arc<HttpClientProvider>,
   initial_cwd: PathBuf,
+  pub is_cjs_resolver: Arc<LspIsCjsResolver>,
   jsr_search_api: CliJsrSearchApi,
   /// Handles module registries, which allow discovery of modules
   module_registry: ModuleRegistry,
@@ -480,6 +484,7 @@ impl Inner {
     let initial_cwd = std::env::current_dir().unwrap_or_else(|_| {
       panic!("Could not resolve current working directory")
     });
+    let is_cjs_resolver = Arc::new(LspIsCjsResolver::new(&cache));
 
     Self {
       assets,
@@ -491,6 +496,7 @@ impl Inner {
       documents,
       http_client_provider,
       initial_cwd: initial_cwd.clone(),
+      is_cjs_resolver,
       jsr_search_api,
       project_version: 0,
       task_queue: Default::default(),
@@ -601,6 +607,7 @@ impl Inner {
       project_version: self.project_version,
       assets: self.assets.snapshot(),
       config: Arc::new(self.config.clone()),
+      is_cjs_resolver: self.is_cjs_resolver.clone(),
       documents: Arc::new(self.documents.clone()),
       resolver: self.resolver.snapshot(),
     })
@@ -622,6 +629,7 @@ impl Inner {
       }
     });
     self.cache = LspCache::new(global_cache_url);
+    self.is_cjs_resolver = Arc::new(LspIsCjsResolver::new(&self.cache));
     let deno_dir = self.cache.deno_dir();
     let workspace_settings = self.config.workspace_settings();
     let maybe_root_path = self
@@ -982,7 +990,7 @@ impl Inner {
           spawn(async move {
             let specifier = {
               let inner = ls.inner.read().await;
-              let resolver = inner.resolver.as_graph_resolver(Some(&referrer));
+              let resolver = inner.resolver.as_cli_resolver(Some(&referrer));
               let Ok(specifier) = resolver.resolve(
                 &specifier,
                 &deno_graph::Range {
@@ -990,6 +998,7 @@ impl Inner {
                   start: deno_graph::Position::zeroed(),
                   end: deno_graph::Position::zeroed(),
                 },
+                NodeModuleKind::Esm,
                 deno_graph::source::ResolutionMode::Types,
               ) else {
                 return;
@@ -1622,6 +1631,10 @@ impl Inner {
       let file_diagnostics = self
         .diagnostics_server
         .get_ts_diagnostics(&specifier, asset_or_doc.document_lsp_version());
+      let specifier_kind = asset_or_doc
+        .document()
+        .map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
+        .unwrap_or(NodeModuleKind::Esm);
       let mut includes_no_cache = false;
       for diagnostic in &fixable_diagnostics {
         match diagnostic.source.as_deref() {
@@ -1660,7 +1673,13 @@ impl Inner {
               .await;
             for action in actions {
               code_actions
-                .add_ts_fix_action(&specifier, &action, diagnostic, self)
+                .add_ts_fix_action(
+                  &specifier,
+                  specifier_kind,
+                  &action,
+                  diagnostic,
+                  self,
+                )
                 .map_err(|err| {
                   error!("Unable to convert fix: {:#}", err);
                   LspError::internal_error()
@@ -1806,10 +1825,9 @@ impl Inner {
           error!("Unable to decode code action data: {:#}", err);
           LspError::invalid_params("The CodeAction's data is invalid.")
         })?;
-      let scope = self
-        .get_asset_or_document(&code_action_data.specifier)
-        .ok()
-        .and_then(|d| d.scope().cloned());
+      let maybe_asset_or_doc =
+        self.get_asset_or_document(&code_action_data.specifier).ok();
+      let scope = maybe_asset_or_doc.as_ref().and_then(|d| d.scope().cloned());
       let combined_code_actions = self
         .ts_server
         .get_combined_code_fix(
@@ -1836,6 +1854,11 @@ impl Inner {
       let changes = if code_action_data.fix_id == "fixMissingImport" {
         fix_ts_import_changes(
           &code_action_data.specifier,
+          maybe_asset_or_doc
+            .as_ref()
+            .and_then(|d| d.document())
+            .map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
+            .unwrap_or(NodeModuleKind::Esm),
           &combined_code_actions.changes,
           self,
         )
@@ -1889,6 +1912,10 @@ impl Inner {
       if kind_suffix == ".rewrite.function.returnType" {
         refactor_edit_info.edits = fix_ts_import_changes(
           &action_data.specifier,
+          asset_or_doc
+            .document()
+            .map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
+            .unwrap_or(NodeModuleKind::Esm),
           &refactor_edit_info.edits,
           self,
         )
@@ -2238,6 +2265,7 @@ impl Inner {
         &self.jsr_search_api,
         &self.npm_search_api,
         &self.documents,
+        &self.is_cjs_resolver,
         self.resolver.as_ref(),
         self
           .config
diff --git a/cli/lsp/repl.rs b/cli/lsp/repl.rs
index fa5809045ed954..b4aaa8cd0d0594 100644
--- a/cli/lsp/repl.rs
+++ b/cli/lsp/repl.rs
@@ -263,7 +263,7 @@ impl ReplLanguageServer {
   }
 
   fn get_document_uri(&self) -> Uri {
-    uri_parse_unencoded(self.cwd_uri.join("$deno$repl.ts").unwrap().as_str())
+    uri_parse_unencoded(self.cwd_uri.join("$deno$repl.mts").unwrap().as_str())
       .unwrap()
   }
 }
diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs
index f5df24d575b925..37f63b912cbef7 100644
--- a/cli/lsp/resolver.rs
+++ b/cli/lsp/resolver.rs
@@ -2,16 +2,18 @@
 
 use dashmap::DashMap;
 use deno_ast::MediaType;
-use deno_ast::ParsedSource;
 use deno_cache_dir::npm::NpmCacheDir;
 use deno_cache_dir::HttpCache;
+use deno_config::deno_json::JsxImportSourceConfig;
 use deno_config::workspace::PackageJsonDepResolution;
 use deno_config::workspace::WorkspaceResolver;
 use deno_core::url::Url;
-use deno_graph::source::Resolver;
+use deno_graph::source::ResolutionMode;
 use deno_graph::GraphImport;
 use deno_graph::ModuleSpecifier;
+use deno_graph::Range;
 use deno_npm::NpmSystemInfo;
+use deno_path_util::url_from_directory_path;
 use deno_path_util::url_to_file_path;
 use deno_runtime::deno_fs;
 use deno_runtime::deno_node::NodeResolver;
@@ -24,6 +26,7 @@ use deno_semver::package::PackageReq;
 use indexmap::IndexMap;
 use node_resolver::errors::ClosestPkgJsonError;
 use node_resolver::InNpmPackageChecker;
+use node_resolver::NodeModuleKind;
 use node_resolver::NodeResolutionMode;
 use std::borrow::Cow;
 use std::collections::BTreeMap;
@@ -33,6 +36,7 @@ use std::collections::HashSet;
 use std::sync::Arc;
 
 use super::cache::LspCache;
+use super::documents::Document;
 use super::jsr::JsrCacheResolver;
 use crate::args::create_default_npmrc;
 use crate::args::CacheSetting;
@@ -53,21 +57,20 @@ use crate::npm::CliNpmResolverCreateOptions;
 use crate::npm::CliNpmResolverManagedSnapshotOption;
 use crate::npm::CreateInNpmPkgCheckerOptions;
 use crate::npm::ManagedCliNpmResolver;
-use crate::resolver::CjsTracker;
-use crate::resolver::CjsTrackerOptions;
 use crate::resolver::CliDenoResolverFs;
-use crate::resolver::CliGraphResolver;
-use crate::resolver::CliGraphResolverOptions;
 use crate::resolver::CliNodeResolver;
+use crate::resolver::CliResolver;
+use crate::resolver::CliResolverOptions;
+use crate::resolver::IsCjsResolver;
 use crate::resolver::WorkerCliNpmGraphResolver;
 use crate::tsc::into_specifier_and_media_type;
+use crate::util::fs::canonicalize_path_maybe_not_exists;
 use crate::util::progress_bar::ProgressBar;
 use crate::util::progress_bar::ProgressBarStyle;
 
 #[derive(Debug, Clone)]
 struct LspScopeResolver {
-  cjs_tracker: Option<Arc<LspCjsTracker>>,
-  graph_resolver: Arc<CliGraphResolver>,
+  resolver: Arc<CliResolver>,
   jsr_resolver: Option<Arc<JsrCacheResolver>>,
   npm_resolver: Option<Arc<dyn CliNpmResolver>>,
   node_resolver: Option<Arc<CliNodeResolver>>,
@@ -81,8 +84,7 @@ struct LspScopeResolver {
 impl Default for LspScopeResolver {
   fn default() -> Self {
     Self {
-      cjs_tracker: None,
-      graph_resolver: create_graph_resolver(None, None, None),
+      resolver: create_cli_resolver(None, None, None),
       jsr_resolver: None,
       npm_resolver: None,
       node_resolver: None,
@@ -103,7 +105,6 @@ impl LspScopeResolver {
   ) -> Self {
     let mut npm_resolver = None;
     let mut node_resolver = None;
-    let mut lsp_cjs_tracker = None;
     let fs = Arc::new(deno_fs::RealFs);
     let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
       deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
@@ -118,14 +119,7 @@ impl LspScopeResolver {
       .await;
       if let Some(npm_resolver) = &npm_resolver {
         let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver);
-        let cjs_tracker = create_cjs_tracker(
-          in_npm_pkg_checker.clone(),
-          pkg_json_resolver.clone(),
-        );
-        lsp_cjs_tracker =
-          Some(Arc::new(LspCjsTracker::new(cjs_tracker.clone())));
         node_resolver = Some(create_node_resolver(
-          cjs_tracker,
           fs.clone(),
           in_npm_pkg_checker,
           npm_resolver,
@@ -133,7 +127,7 @@ impl LspScopeResolver {
         ));
       }
     }
-    let graph_resolver = create_graph_resolver(
+    let cli_resolver = create_cli_resolver(
       config_data.map(|d| d.as_ref()),
       npm_resolver.as_ref(),
       node_resolver.as_ref(),
@@ -146,7 +140,9 @@ 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 = graph_resolver.create_graph_npm_resolver();
+    let npm_graph_resolver = cli_resolver.create_graph_npm_resolver();
+    let maybe_jsx_import_source_config =
+      config_data.and_then(|d| d.maybe_jsx_import_source_config());
     let graph_imports = config_data
       .and_then(|d| d.member_dir.workspace.to_compiler_option_types().ok())
       .map(|imports| {
@@ -154,11 +150,18 @@ impl LspScopeResolver {
           imports
             .into_iter()
             .map(|(referrer, imports)| {
+              let resolver = SingleReferrerGraphResolver {
+                valid_referrer: &referrer,
+                referrer_kind: NodeModuleKind::Esm,
+                cli_resolver: &cli_resolver,
+                jsx_import_source_config: maybe_jsx_import_source_config
+                  .as_ref(),
+              };
               let graph_import = GraphImport::new(
                 &referrer,
                 imports,
                 &CliJsrUrlProvider,
-                Some(graph_resolver.as_ref()),
+                Some(&resolver),
                 Some(&npm_graph_resolver),
               );
               (referrer, graph_import)
@@ -182,6 +185,8 @@ impl LspScopeResolver {
               .resolve_req_reference(
                 &req_ref,
                 &referrer,
+                // todo(dsherret): this is wrong because it doesn't consider CJS referrers
+                NodeModuleKind::Esm,
                 NodeResolutionMode::Types,
               )
               .ok()?,
@@ -195,8 +200,7 @@ impl LspScopeResolver {
     let package_json_deps_by_resolution =
       Arc::new(package_json_deps_by_resolution.unwrap_or_default());
     Self {
-      cjs_tracker: lsp_cjs_tracker,
-      graph_resolver,
+      resolver: cli_resolver,
       jsr_resolver,
       npm_resolver,
       node_resolver,
@@ -216,30 +220,22 @@ impl LspScopeResolver {
       deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
     ));
     let mut node_resolver = None;
-    let mut lsp_cjs_tracker = None;
     if let Some(npm_resolver) = &npm_resolver {
       let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver);
-      let cjs_tracker = create_cjs_tracker(
-        in_npm_pkg_checker.clone(),
-        pkg_json_resolver.clone(),
-      );
-      lsp_cjs_tracker = Some(Arc::new(LspCjsTracker::new(cjs_tracker.clone())));
       node_resolver = Some(create_node_resolver(
-        cjs_tracker,
         fs,
         in_npm_pkg_checker,
         npm_resolver,
         pkg_json_resolver.clone(),
       ));
     }
-    let graph_resolver = create_graph_resolver(
+    let graph_resolver = create_cli_resolver(
       self.config_data.as_deref(),
       npm_resolver.as_ref(),
       node_resolver.as_ref(),
     );
     Arc::new(Self {
-      cjs_tracker: lsp_cjs_tracker,
-      graph_resolver,
+      resolver: graph_resolver,
       jsr_resolver: self.jsr_resolver.clone(),
       npm_resolver,
       node_resolver,
@@ -334,12 +330,12 @@ impl LspResolver {
     }
   }
 
-  pub fn as_graph_resolver(
+  pub fn as_cli_resolver(
     &self,
     file_referrer: Option<&ModuleSpecifier>,
-  ) -> &dyn Resolver {
+  ) -> &CliResolver {
     let resolver = self.get_scope_resolver(file_referrer);
-    resolver.graph_resolver.as_ref()
+    resolver.resolver.as_ref()
   }
 
   pub fn create_graph_npm_resolver(
@@ -347,15 +343,15 @@ impl LspResolver {
     file_referrer: Option<&ModuleSpecifier>,
   ) -> WorkerCliNpmGraphResolver {
     let resolver = self.get_scope_resolver(file_referrer);
-    resolver.graph_resolver.create_graph_npm_resolver()
+    resolver.resolver.create_graph_npm_resolver()
   }
 
-  pub fn maybe_cjs_tracker(
+  pub fn as_config_data(
     &self,
     file_referrer: Option<&ModuleSpecifier>,
-  ) -> Option<&Arc<LspCjsTracker>> {
+  ) -> Option<&Arc<ConfigData>> {
     let resolver = self.get_scope_resolver(file_referrer);
-    resolver.cjs_tracker.as_ref()
+    resolver.config_data.as_ref()
   }
 
   pub fn maybe_node_resolver(
@@ -429,13 +425,19 @@ impl LspResolver {
     &self,
     req_ref: &NpmPackageReqReference,
     referrer: &ModuleSpecifier,
+    referrer_kind: NodeModuleKind,
     file_referrer: Option<&ModuleSpecifier>,
   ) -> Option<(ModuleSpecifier, MediaType)> {
     let resolver = self.get_scope_resolver(file_referrer);
     let node_resolver = resolver.node_resolver.as_ref()?;
     Some(into_specifier_and_media_type(Some(
       node_resolver
-        .resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types)
+        .resolve_req_reference(
+          req_ref,
+          referrer,
+          referrer_kind,
+          NodeResolutionMode::Types,
+        )
         .ok()?,
     )))
   }
@@ -478,6 +480,7 @@ impl LspResolver {
     &self,
     specifier_text: &str,
     referrer: &ModuleSpecifier,
+    referrer_kind: NodeModuleKind,
   ) -> bool {
     let resolver = self.get_scope_resolver(Some(referrer));
     let Some(node_resolver) = resolver.node_resolver.as_ref() else {
@@ -487,6 +490,7 @@ impl LspResolver {
       .resolve_if_for_npm_pkg(
         specifier_text,
         referrer,
+        referrer_kind,
         NodeResolutionMode::Types,
       )
       .ok()
@@ -615,21 +619,6 @@ async fn create_npm_resolver(
   Some(create_cli_npm_resolver_for_lsp(options).await)
 }
 
-fn create_cjs_tracker(
-  in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
-  pkg_json_resolver: Arc<PackageJsonResolver>,
-) -> Arc<CjsTracker> {
-  Arc::new(CjsTracker::new(
-    in_npm_pkg_checker,
-    pkg_json_resolver,
-    CjsTrackerOptions {
-      // todo(dsherret): support in the lsp by stabilizing the feature
-      // so that we don't have to pipe the config in here
-      unstable_detect_cjs: false,
-    },
-  ))
-}
-
 fn create_in_npm_pkg_checker(
   npm_resolver: &Arc<dyn CliNpmResolver>,
 ) -> Arc<dyn InNpmPackageChecker> {
@@ -649,7 +638,6 @@ fn create_in_npm_pkg_checker(
 }
 
 fn create_node_resolver(
-  cjs_tracker: Arc<CjsTracker>,
   fs: Arc<dyn deno_fs::FileSystem>,
   in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
   npm_resolver: &Arc<dyn CliNpmResolver>,
@@ -662,7 +650,6 @@ fn create_node_resolver(
     pkg_json_resolver.clone(),
   ));
   Arc::new(CliNodeResolver::new(
-    cjs_tracker.clone(),
     fs,
     in_npm_pkg_checker,
     node_resolver_inner,
@@ -670,13 +657,12 @@ fn create_node_resolver(
   ))
 }
 
-fn create_graph_resolver(
+fn create_cli_resolver(
   config_data: Option<&ConfigData>,
   npm_resolver: Option<&Arc<dyn CliNpmResolver>>,
   node_resolver: Option<&Arc<CliNodeResolver>>,
-) -> Arc<CliGraphResolver> {
-  let workspace = config_data.map(|d| &d.member_dir.workspace);
-  Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
+) -> Arc<CliResolver> {
+  Arc::new(CliResolver::new(CliResolverOptions {
     node_resolver: node_resolver.cloned(),
     npm_resolver: npm_resolver.cloned(),
     workspace_resolver: config_data.map(|d| d.resolver.clone()).unwrap_or_else(
@@ -691,9 +677,6 @@ fn create_graph_resolver(
         ))
       },
     ),
-    maybe_jsx_import_source_config: workspace.and_then(|workspace| {
-      workspace.to_maybe_jsx_import_source_config().ok().flatten()
-    }),
     maybe_vendor_dir: config_data.and_then(|d| d.vendor_dir.as_ref()),
     bare_node_builtins_enabled: config_data
       .is_some_and(|d| d.unstable.contains("bare-node-builtins")),
@@ -726,6 +709,141 @@ impl std::fmt::Debug for RedirectResolver {
   }
 }
 
+#[derive(Debug)]
+pub struct LspIsCjsResolver {
+  inner: IsCjsResolver,
+}
+
+impl Default for LspIsCjsResolver {
+  fn default() -> Self {
+    LspIsCjsResolver::new(&Default::default())
+  }
+}
+
+impl LspIsCjsResolver {
+  pub fn new(cache: &LspCache) -> Self {
+    #[derive(Debug)]
+    struct LspInNpmPackageChecker {
+      global_cache_dir: ModuleSpecifier,
+    }
+
+    impl LspInNpmPackageChecker {
+      pub fn new(cache: &LspCache) -> Self {
+        let npm_folder_path = cache.deno_dir().npm_folder_path();
+        Self {
+          global_cache_dir: url_from_directory_path(
+            &canonicalize_path_maybe_not_exists(&npm_folder_path)
+              .unwrap_or(npm_folder_path),
+          )
+          .unwrap_or_else(|_| {
+            ModuleSpecifier::parse("file:///invalid/").unwrap()
+          }),
+        }
+      }
+    }
+
+    impl InNpmPackageChecker for LspInNpmPackageChecker {
+      fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
+        if specifier.scheme() != "file" {
+          return false;
+        }
+        if specifier
+          .as_str()
+          .starts_with(self.global_cache_dir.as_str())
+        {
+          return true;
+        }
+        specifier.as_str().contains("/node_modules/")
+      }
+    }
+
+    let fs = Arc::new(deno_fs::RealFs);
+    let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
+      deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
+    ));
+
+    LspIsCjsResolver {
+      inner: IsCjsResolver::new(
+        Arc::new(LspInNpmPackageChecker::new(cache)),
+        pkg_json_resolver,
+        crate::resolver::IsCjsResolverOptions {
+          detect_cjs: true,
+          is_node_main: false,
+        },
+      ),
+    }
+  }
+
+  pub fn get_maybe_doc_module_kind(
+    &self,
+    specifier: &ModuleSpecifier,
+    maybe_document: Option<&Document>,
+  ) -> NodeModuleKind {
+    self.get_lsp_referrer_kind(
+      specifier,
+      maybe_document.and_then(|d| d.is_script()),
+    )
+  }
+
+  pub fn get_doc_module_kind(&self, document: &Document) -> NodeModuleKind {
+    self.get_lsp_referrer_kind(document.specifier(), document.is_script())
+  }
+
+  pub fn get_lsp_referrer_kind(
+    &self,
+    specifier: &ModuleSpecifier,
+    is_script: Option<bool>,
+  ) -> NodeModuleKind {
+    self.inner.get_lsp_referrer_kind(specifier, is_script)
+  }
+}
+
+#[derive(Debug)]
+pub struct SingleReferrerGraphResolver<'a> {
+  pub valid_referrer: &'a ModuleSpecifier,
+  pub referrer_kind: NodeModuleKind,
+  pub cli_resolver: &'a CliResolver,
+  pub jsx_import_source_config: Option<&'a JsxImportSourceConfig>,
+}
+
+impl<'a> deno_graph::source::Resolver for SingleReferrerGraphResolver<'a> {
+  fn default_jsx_import_source(&self) -> Option<String> {
+    self
+      .jsx_import_source_config
+      .and_then(|c| c.default_specifier.clone())
+  }
+
+  fn default_jsx_import_source_types(&self) -> Option<String> {
+    self
+      .jsx_import_source_config
+      .and_then(|c| c.default_types_specifier.clone())
+  }
+
+  fn jsx_import_source_module(&self) -> &str {
+    self
+      .jsx_import_source_config
+      .map(|c| c.module.as_str())
+      .unwrap_or(deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE)
+  }
+
+  fn resolve(
+    &self,
+    specifier_text: &str,
+    referrer_range: &Range,
+    mode: ResolutionMode,
+  ) -> Result<ModuleSpecifier, deno_graph::source::ResolveError> {
+    // this resolver assumes it will only be used with a single referrer
+    // with the provided referrer kind
+    debug_assert_eq!(referrer_range.specifier, *self.valid_referrer);
+    self.cli_resolver.resolve(
+      specifier_text,
+      referrer_range,
+      self.referrer_kind,
+      mode,
+    )
+  }
+}
+
 impl RedirectResolver {
   fn new(
     cache: Arc<dyn HttpCache>,
@@ -842,45 +960,6 @@ impl RedirectResolver {
   }
 }
 
-#[derive(Debug)]
-pub struct LspCjsTracker {
-  cjs_tracker: Arc<CjsTracker>,
-}
-
-impl LspCjsTracker {
-  pub fn new(cjs_tracker: Arc<CjsTracker>) -> Self {
-    Self { cjs_tracker }
-  }
-
-  pub fn is_cjs(
-    &self,
-    specifier: &ModuleSpecifier,
-    media_type: MediaType,
-    maybe_parsed_source: Option<&ParsedSource>,
-  ) -> bool {
-    if let Some(module_kind) =
-      self.cjs_tracker.get_known_kind(specifier, media_type)
-    {
-      module_kind.is_cjs()
-    } else {
-      let maybe_is_script = maybe_parsed_source.map(|p| p.compute_is_script());
-      maybe_is_script
-        .and_then(|is_script| {
-          self
-            .cjs_tracker
-            .is_cjs_with_known_is_script(specifier, media_type, is_script)
-            .ok()
-        })
-        .unwrap_or_else(|| {
-          self
-            .cjs_tracker
-            .is_maybe_cjs(specifier, media_type)
-            .unwrap_or(false)
-        })
-    }
-  }
-}
-
 #[cfg(test)]
 mod tests {
   use super::*;
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 6f63ced5be8a0a..c9b24176ad2e0a 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -69,6 +69,7 @@ use indexmap::IndexMap;
 use indexmap::IndexSet;
 use lazy_regex::lazy_regex;
 use log::error;
+use node_resolver::NodeModuleKind;
 use once_cell::sync::Lazy;
 use regex::Captures;
 use regex::Regex;
@@ -4401,25 +4402,15 @@ fn op_load<'s>(
       None
     } else {
       let asset_or_document = state.get_asset_or_document(&specifier);
-      asset_or_document.map(|doc| {
-        let maybe_cjs_tracker = state
-          .state_snapshot
-          .resolver
-          .maybe_cjs_tracker(Some(&specifier));
-        LoadResponse {
-          data: doc.text(),
-          script_kind: crate::tsc::as_ts_script_kind(doc.media_type()),
-          version: state.script_version(&specifier),
-          is_cjs: maybe_cjs_tracker
-            .map(|t| {
-              t.is_cjs(
-                &specifier,
-                doc.media_type(),
-                doc.maybe_parsed_source().and_then(|p| p.as_ref().ok()),
-              )
-            })
-            .unwrap_or(false),
-        }
+      asset_or_document.map(|doc| LoadResponse {
+        data: doc.text(),
+        script_kind: crate::tsc::as_ts_script_kind(doc.media_type()),
+        version: state.script_version(&specifier),
+        is_cjs: doc
+          .document()
+          .map(|d| state.state_snapshot.is_cjs_resolver.get_doc_module_kind(d))
+          .unwrap_or(NodeModuleKind::Esm)
+          == NodeModuleKind::Cjs,
       })
     };
 
@@ -4662,6 +4653,10 @@ fn op_script_names(state: &mut OpState) -> ScriptNames {
         let (types, _) = documents.resolve_dependency(
           types,
           specifier,
+          state
+            .state_snapshot
+            .is_cjs_resolver
+            .get_doc_module_kind(doc),
           doc.file_referrer(),
         )?;
         let types_doc = documents.get_or_load(&types, doc.file_referrer())?;
@@ -5544,6 +5539,7 @@ mod tests {
       documents: Arc::new(documents),
       assets: Default::default(),
       config: Arc::new(config),
+      is_cjs_resolver: Default::default(),
       resolver,
     });
     let performance = Arc::new(Performance::default());
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index 43c9e1aa0750e0..f9c974d77ee28e 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -27,8 +27,8 @@ use crate::node;
 use crate::node::CliNodeCodeTranslator;
 use crate::npm::CliNpmResolver;
 use crate::resolver::CjsTracker;
-use crate::resolver::CliGraphResolver;
 use crate::resolver::CliNodeResolver;
+use crate::resolver::CliResolver;
 use crate::resolver::ModuleCodeStringSource;
 use crate::resolver::NotSupportedKindInNpmError;
 use crate::resolver::NpmModuleLoader;
@@ -60,7 +60,6 @@ use deno_core::RequestedModuleType;
 use deno_core::ResolutionKind;
 use deno_core::SourceCodeCacheInfo;
 use deno_graph::source::ResolutionMode;
-use deno_graph::source::Resolver;
 use deno_graph::GraphKind;
 use deno_graph::JsModule;
 use deno_graph::JsonModule;
@@ -73,6 +72,7 @@ use deno_runtime::deno_node::create_host_defined_options;
 use deno_runtime::deno_node::NodeRequireLoader;
 use deno_runtime::deno_permissions::PermissionsContainer;
 use deno_semver::npm::NpmPackageReqReference;
+use node_resolver::errors::ClosestPkgJsonError;
 use node_resolver::InNpmPackageChecker;
 use node_resolver::NodeResolutionMode;
 
@@ -206,7 +206,6 @@ struct SharedCliModuleLoaderState {
   lib_worker: TsTypeLib,
   initial_cwd: PathBuf,
   is_inspecting: bool,
-  is_npm_main: bool,
   is_repl: bool,
   cjs_tracker: Arc<CjsTracker>,
   code_cache: Option<Arc<CodeCache>>,
@@ -220,7 +219,7 @@ struct SharedCliModuleLoaderState {
   npm_resolver: Arc<dyn CliNpmResolver>,
   npm_module_loader: NpmModuleLoader,
   parsed_source_cache: Arc<ParsedSourceCache>,
-  resolver: Arc<CliGraphResolver>,
+  resolver: Arc<CliResolver>,
 }
 
 pub struct CliModuleLoaderFactory {
@@ -243,7 +242,7 @@ impl CliModuleLoaderFactory {
     npm_resolver: Arc<dyn CliNpmResolver>,
     npm_module_loader: NpmModuleLoader,
     parsed_source_cache: Arc<ParsedSourceCache>,
-    resolver: Arc<CliGraphResolver>,
+    resolver: Arc<CliResolver>,
   ) -> Self {
     Self {
       shared: Arc::new(SharedCliModuleLoaderState {
@@ -252,7 +251,6 @@ impl CliModuleLoaderFactory {
         lib_worker: options.ts_type_lib_worker(),
         initial_cwd: options.initial_cwd().to_path_buf(),
         is_inspecting: options.is_inspecting(),
-        is_npm_main: options.is_npm_main(),
         is_repl: matches!(
           options.sub_command(),
           DenoSubcommand::Repl(_) | DenoSubcommand::Jupyter(_)
@@ -286,7 +284,6 @@ impl CliModuleLoaderFactory {
       Rc::new(CliModuleLoader(Rc::new(CliModuleLoaderInner {
         lib,
         is_worker,
-        is_npm_main: self.shared.is_npm_main,
         parent_permissions,
         permissions,
         graph_container: graph_container.clone(),
@@ -295,13 +292,14 @@ impl CliModuleLoaderFactory {
         parsed_source_cache: self.shared.parsed_source_cache.clone(),
         shared: self.shared.clone(),
       })));
-    let node_require_loader = Rc::new(CliNodeRequireLoader::new(
-      self.shared.emitter.clone(),
-      self.shared.fs.clone(),
+    let node_require_loader = Rc::new(CliNodeRequireLoader {
+      cjs_tracker: self.shared.cjs_tracker.clone(),
+      emitter: self.shared.emitter.clone(),
+      fs: self.shared.fs.clone(),
       graph_container,
-      self.shared.in_npm_pkg_checker.clone(),
-      self.shared.npm_resolver.clone(),
-    ));
+      in_npm_pkg_checker: self.shared.in_npm_pkg_checker.clone(),
+      npm_resolver: self.shared.npm_resolver.clone(),
+    });
     CreateModuleLoaderResult {
       module_loader,
       node_require_loader,
@@ -343,7 +341,6 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
 
 struct CliModuleLoaderInner<TGraphContainer: ModuleGraphContainer> {
   lib: TsTypeLib,
-  is_npm_main: bool,
   is_worker: bool,
   /// The initial set of permissions used to resolve the static imports in the
   /// worker. These are "allow all" for main worker, and parent thread
@@ -450,7 +447,7 @@ impl<TGraphContainer: ModuleGraphContainer>
     let referrer = if referrer.is_empty() && self.shared.is_repl {
       // FIXME(bartlomieju): this is a hacky way to provide compatibility with REPL
       // and `Deno.core.evalContext` API. Ideally we should always have a referrer filled
-      "./$deno$repl.ts"
+      "./$deno$repl.mts"
     } else {
       referrer
     };
@@ -478,7 +475,12 @@ impl<TGraphContainer: ModuleGraphContainer>
         self
           .shared
           .node_resolver
-          .resolve(raw_specifier, referrer, NodeResolutionMode::Execution)?
+          .resolve(
+            raw_specifier,
+            referrer,
+            self.shared.cjs_tracker.get_referrer_kind(referrer),
+            NodeResolutionMode::Execution,
+          )?
           .into_url(),
       );
     }
@@ -508,6 +510,7 @@ impl<TGraphContainer: ModuleGraphContainer>
           start: deno_graph::Position::zeroed(),
           end: deno_graph::Position::zeroed(),
         },
+        self.shared.cjs_tracker.get_referrer_kind(referrer),
         ResolutionMode::Execution,
       )?),
     };
@@ -518,6 +521,7 @@ impl<TGraphContainer: ModuleGraphContainer>
         return self.shared.node_resolver.resolve_req_reference(
           &reference,
           referrer,
+          self.shared.cjs_tracker.get_referrer_kind(referrer),
           NodeResolutionMode::Execution,
         );
       }
@@ -538,6 +542,7 @@ impl<TGraphContainer: ModuleGraphContainer>
             &package_folder,
             module.nv_reference.sub_path(),
             Some(referrer),
+            self.shared.cjs_tracker.get_referrer_kind(referrer),
             NodeResolutionMode::Execution,
           )
           .with_context(|| {
@@ -668,14 +673,11 @@ impl<TGraphContainer: ModuleGraphContainer>
         is_script,
         ..
       })) => {
-        // todo(dsherret): revert in https://github.com/denoland/deno/pull/26439
-        if self.is_npm_main && *is_script
-          || self.shared.cjs_tracker.is_cjs_with_known_is_script(
-            specifier,
-            *media_type,
-            *is_script,
-          )?
-        {
+        if self.shared.cjs_tracker.is_cjs_with_known_is_script(
+          specifier,
+          *media_type,
+          *is_script,
+        )? {
           return Ok(Some(CodeOrDeferredEmit::Cjs {
             specifier,
             media_type: *media_type,
@@ -1031,6 +1033,7 @@ impl ModuleGraphUpdatePermit for WorkerModuleGraphUpdatePermit {
 
 #[derive(Debug)]
 struct CliNodeRequireLoader<TGraphContainer: ModuleGraphContainer> {
+  cjs_tracker: Arc<CjsTracker>,
   emitter: Arc<Emitter>,
   fs: Arc<dyn FileSystem>,
   graph_container: TGraphContainer,
@@ -1038,26 +1041,6 @@ struct CliNodeRequireLoader<TGraphContainer: ModuleGraphContainer> {
   npm_resolver: Arc<dyn CliNpmResolver>,
 }
 
-impl<TGraphContainer: ModuleGraphContainer>
-  CliNodeRequireLoader<TGraphContainer>
-{
-  pub fn new(
-    emitter: Arc<Emitter>,
-    fs: Arc<dyn FileSystem>,
-    graph_container: TGraphContainer,
-    in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
-    npm_resolver: Arc<dyn CliNpmResolver>,
-  ) -> Self {
-    Self {
-      emitter,
-      fs,
-      graph_container,
-      in_npm_pkg_checker,
-      npm_resolver,
-    }
-  }
-}
-
 impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
   for CliNodeRequireLoader<TGraphContainer>
 {
@@ -1103,4 +1086,12 @@ impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
       Ok(text)
     }
   }
+
+  fn is_maybe_cjs(
+    &self,
+    specifier: &ModuleSpecifier,
+  ) -> Result<bool, ClosestPkgJsonError> {
+    let media_type = MediaType::from_specifier(specifier);
+    self.cjs_tracker.is_maybe_cjs(specifier, media_type)
+  }
 }
diff --git a/cli/node.rs b/cli/node.rs
index 1d410a726af601..8235745a9114c3 100644
--- a/cli/node.rs
+++ b/cli/node.rs
@@ -62,10 +62,6 @@ pub struct CliCjsCodeAnalyzer {
   cjs_tracker: Arc<CjsTracker>,
   fs: deno_fs::FileSystemRc,
   parsed_source_cache: Option<Arc<ParsedSourceCache>>,
-  // todo(dsherret): hack, remove in https://github.com/denoland/deno/pull/26439
-  // For example, this does not properly handle if cjs analysis was already done
-  // and has been cached.
-  is_npm_main: bool,
 }
 
 impl CliCjsCodeAnalyzer {
@@ -74,14 +70,12 @@ impl CliCjsCodeAnalyzer {
     cjs_tracker: Arc<CjsTracker>,
     fs: deno_fs::FileSystemRc,
     parsed_source_cache: Option<Arc<ParsedSourceCache>>,
-    is_npm_main: bool,
   ) -> Self {
     Self {
       cache,
       cjs_tracker,
       fs,
       parsed_source_cache,
-      is_npm_main,
     }
   }
 
@@ -106,9 +100,7 @@ impl CliCjsCodeAnalyzer {
     }
 
     let cjs_tracker = self.cjs_tracker.clone();
-    let is_npm_main = self.is_npm_main;
-    let is_maybe_cjs =
-      cjs_tracker.is_maybe_cjs(specifier, media_type)? || is_npm_main;
+    let is_maybe_cjs = cjs_tracker.is_maybe_cjs(specifier, media_type)?;
     let analysis = if is_maybe_cjs {
       let maybe_parsed_source = self
         .parsed_source_cache
@@ -135,7 +127,7 @@ impl CliCjsCodeAnalyzer {
             parsed_source.specifier(),
             media_type,
             is_script,
-          )? || is_script && is_npm_main;
+          )?;
           if is_cjs {
             let analysis = parsed_source.analyze_cjs();
             Ok(CliCjsAnalysis::Cjs {
diff --git a/cli/resolver.rs b/cli/resolver.rs
index 710b97509345a6..786e5d0dbc810a 100644
--- a/cli/resolver.rs
+++ b/cli/resolver.rs
@@ -4,7 +4,6 @@ use async_trait::async_trait;
 use dashmap::DashMap;
 use dashmap::DashSet;
 use deno_ast::MediaType;
-use deno_ast::ModuleKind;
 use deno_config::workspace::MappedResolution;
 use deno_config::workspace::MappedResolutionDiagnostic;
 use deno_config::workspace::MappedResolutionError;
@@ -17,9 +16,7 @@ use deno_core::ModuleSourceCode;
 use deno_core::ModuleSpecifier;
 use deno_graph::source::ResolutionMode;
 use deno_graph::source::ResolveError;
-use deno_graph::source::Resolver;
 use deno_graph::source::UnknownBuiltInNodeModuleError;
-use deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE;
 use deno_graph::NpmLoadError;
 use deno_graph::NpmResolvePkgReqsResult;
 use deno_npm::resolution::NpmResolutionError;
@@ -52,7 +49,6 @@ use std::path::PathBuf;
 use std::sync::Arc;
 use thiserror::Error;
 
-use crate::args::JsxImportSourceConfig;
 use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
 use crate::node::CliNodeCodeTranslator;
 use crate::npm::CliNpmResolver;
@@ -108,7 +104,6 @@ impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs {
 
 #[derive(Debug)]
 pub struct CliNodeResolver {
-  cjs_tracker: Arc<CjsTracker>,
   fs: Arc<dyn deno_fs::FileSystem>,
   in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
   node_resolver: Arc<NodeResolver>,
@@ -117,14 +112,12 @@ pub struct CliNodeResolver {
 
 impl CliNodeResolver {
   pub fn new(
-    cjs_tracker: Arc<CjsTracker>,
     fs: Arc<dyn deno_fs::FileSystem>,
     in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
     node_resolver: Arc<NodeResolver>,
     npm_resolver: Arc<dyn CliNpmResolver>,
   ) -> Self {
     Self {
-      cjs_tracker,
       fs,
       in_npm_pkg_checker,
       node_resolver,
@@ -140,9 +133,11 @@ impl CliNodeResolver {
     &self,
     specifier: &str,
     referrer: &ModuleSpecifier,
+    referrer_kind: NodeModuleKind,
     mode: NodeResolutionMode,
   ) -> Result<Option<NodeResolution>, AnyError> {
-    let resolution_result = self.resolve(specifier, referrer, mode);
+    let resolution_result =
+      self.resolve(specifier, referrer, referrer_kind, mode);
     match resolution_result {
       Ok(res) => Ok(Some(res)),
       Err(err) => {
@@ -213,35 +208,26 @@ impl CliNodeResolver {
     &self,
     specifier: &str,
     referrer: &ModuleSpecifier,
+    referrer_kind: NodeModuleKind,
     mode: NodeResolutionMode,
   ) -> Result<NodeResolution, NodeResolveError> {
-    let referrer_kind = if self
-      .cjs_tracker
-      .is_maybe_cjs(referrer, MediaType::from_specifier(referrer))
-      .map_err(|err| NodeResolveErrorKind::PackageResolve(err.into()))?
-    {
-      NodeModuleKind::Cjs
-    } else {
-      NodeModuleKind::Esm
-    };
-
-    let res =
-      self
-        .node_resolver
-        .resolve(specifier, referrer, referrer_kind, mode)?;
-    Ok(res)
+    self
+      .node_resolver
+      .resolve(specifier, referrer, referrer_kind, mode)
   }
 
   pub fn resolve_req_reference(
     &self,
     req_ref: &NpmPackageReqReference,
     referrer: &ModuleSpecifier,
+    referrer_kind: NodeModuleKind,
     mode: NodeResolutionMode,
   ) -> Result<ModuleSpecifier, AnyError> {
     self.resolve_req_with_sub_path(
       req_ref.req(),
       req_ref.sub_path(),
       referrer,
+      referrer_kind,
       mode,
     )
   }
@@ -251,6 +237,7 @@ impl CliNodeResolver {
     req: &PackageReq,
     sub_path: Option<&str>,
     referrer: &ModuleSpecifier,
+    referrer_kind: NodeModuleKind,
     mode: NodeResolutionMode,
   ) -> Result<ModuleSpecifier, AnyError> {
     let package_folder = self
@@ -260,6 +247,7 @@ impl CliNodeResolver {
       &package_folder,
       sub_path,
       Some(referrer),
+      referrer_kind,
       mode,
     );
     match resolution_result {
@@ -284,12 +272,14 @@ impl CliNodeResolver {
     package_folder: &Path,
     sub_path: Option<&str>,
     maybe_referrer: Option<&ModuleSpecifier>,
+    referrer_kind: NodeModuleKind,
     mode: NodeResolutionMode,
   ) -> Result<ModuleSpecifier, PackageSubpathResolveError> {
     self.node_resolver.resolve_package_subpath_from_deno_module(
       package_folder,
       sub_path,
       maybe_referrer,
+      referrer_kind,
       mode,
     )
   }
@@ -419,10 +409,6 @@ impl NpmModuleLoader {
   }
 }
 
-pub struct CjsTrackerOptions {
-  pub unstable_detect_cjs: bool,
-}
-
 /// Keeps track of what module specifiers were resolved as CJS.
 ///
 /// Modules that are `.js` or `.ts` are only known to be CJS or
@@ -430,22 +416,22 @@ pub struct CjsTrackerOptions {
 /// will be "maybe CJS" until they're loaded.
 #[derive(Debug)]
 pub struct CjsTracker {
-  in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
-  pkg_json_resolver: Arc<PackageJsonResolver>,
-  unstable_detect_cjs: bool,
-  known: DashMap<ModuleSpecifier, ModuleKind>,
+  is_cjs_resolver: IsCjsResolver,
+  known: DashMap<ModuleSpecifier, NodeModuleKind>,
 }
 
 impl CjsTracker {
   pub fn new(
     in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
     pkg_json_resolver: Arc<PackageJsonResolver>,
-    options: CjsTrackerOptions,
+    options: IsCjsResolverOptions,
   ) -> Self {
     Self {
-      in_npm_pkg_checker,
-      pkg_json_resolver,
-      unstable_detect_cjs: options.unstable_detect_cjs,
+      is_cjs_resolver: IsCjsResolver::new(
+        in_npm_pkg_checker,
+        pkg_json_resolver,
+        options,
+      ),
       known: Default::default(),
     }
   }
@@ -485,47 +471,135 @@ impl CjsTracker {
       .get_known_kind_with_is_script(specifier, media_type, is_script)
     {
       Some(kind) => kind,
-      None => self.check_based_on_pkg_json(specifier)?,
+      None => self.is_cjs_resolver.check_based_on_pkg_json(specifier)?,
     };
-    Ok(kind.is_cjs())
+    Ok(kind == NodeModuleKind::Cjs)
   }
 
   pub fn get_known_kind(
     &self,
     specifier: &ModuleSpecifier,
     media_type: MediaType,
-  ) -> Option<ModuleKind> {
+  ) -> Option<NodeModuleKind> {
     self.get_known_kind_with_is_script(specifier, media_type, None)
   }
 
+  pub fn get_referrer_kind(
+    &self,
+    specifier: &ModuleSpecifier,
+  ) -> NodeModuleKind {
+    if specifier.scheme() != "file" {
+      return NodeModuleKind::Esm;
+    }
+    self
+      .get_known_kind(specifier, MediaType::from_specifier(specifier))
+      .unwrap_or(NodeModuleKind::Esm)
+  }
+
+  fn get_known_kind_with_is_script(
+    &self,
+    specifier: &ModuleSpecifier,
+    media_type: MediaType,
+    is_script: Option<bool>,
+  ) -> Option<NodeModuleKind> {
+    self.is_cjs_resolver.get_known_kind_with_is_script(
+      specifier,
+      media_type,
+      is_script,
+      &self.known,
+    )
+  }
+}
+
+#[derive(Debug)]
+pub struct IsCjsResolverOptions {
+  pub detect_cjs: bool,
+  pub is_node_main: bool,
+}
+
+#[derive(Debug)]
+pub struct IsCjsResolver {
+  in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+  pkg_json_resolver: Arc<PackageJsonResolver>,
+  options: IsCjsResolverOptions,
+}
+
+impl IsCjsResolver {
+  pub fn new(
+    in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+    pkg_json_resolver: Arc<PackageJsonResolver>,
+    options: IsCjsResolverOptions,
+  ) -> Self {
+    Self {
+      in_npm_pkg_checker,
+      pkg_json_resolver,
+      options,
+    }
+  }
+
+  pub fn get_lsp_referrer_kind(
+    &self,
+    specifier: &ModuleSpecifier,
+    is_script: Option<bool>,
+  ) -> NodeModuleKind {
+    if specifier.scheme() != "file" {
+      return NodeModuleKind::Esm;
+    }
+    match MediaType::from_specifier(specifier) {
+      MediaType::Mts | MediaType::Mjs | MediaType::Dmts => NodeModuleKind::Esm,
+      MediaType::Cjs | MediaType::Cts | MediaType::Dcts => NodeModuleKind::Cjs,
+      MediaType::Dts => {
+        // dts files are always determined based on the package.json because
+        // they contain imports/exports even when considered CJS
+        self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm)
+      }
+      MediaType::Wasm |
+      MediaType::Json => NodeModuleKind::Esm,
+      MediaType::JavaScript
+      | MediaType::Jsx
+      | MediaType::TypeScript
+      | MediaType::Tsx
+      // treat these as unknown
+      | MediaType::Css
+      | MediaType::SourceMap
+      | MediaType::Unknown => {
+        match is_script {
+          Some(true) => self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm),
+          Some(false) | None => NodeModuleKind::Esm,
+        }
+      }
+    }
+  }
+
   fn get_known_kind_with_is_script(
     &self,
     specifier: &ModuleSpecifier,
     media_type: MediaType,
     is_script: Option<bool>,
-  ) -> Option<ModuleKind> {
+    known_cache: &DashMap<ModuleSpecifier, NodeModuleKind>,
+  ) -> Option<NodeModuleKind> {
     if specifier.scheme() != "file" {
-      return Some(ModuleKind::Esm);
+      return Some(NodeModuleKind::Esm);
     }
 
     match media_type {
-      MediaType::Mts | MediaType::Mjs | MediaType::Dmts => Some(ModuleKind::Esm),
-      MediaType::Cjs | MediaType::Cts | MediaType::Dcts => Some(ModuleKind::Cjs),
+      MediaType::Mts | MediaType::Mjs | MediaType::Dmts => Some(NodeModuleKind::Esm),
+      MediaType::Cjs | MediaType::Cts | MediaType::Dcts => Some(NodeModuleKind::Cjs),
       MediaType::Dts => {
         // dts files are always determined based on the package.json because
         // they contain imports/exports even when considered CJS
-        if let Some(value) = self.known.get(specifier).map(|v| *v) {
+        if let Some(value) = known_cache.get(specifier).map(|v| *v) {
           Some(value)
         } else {
           let value = self.check_based_on_pkg_json(specifier).ok();
           if let Some(value) = value {
-            self.known.insert(specifier.clone(), value);
+            known_cache.insert(specifier.clone(), value);
           }
-          Some(value.unwrap_or(ModuleKind::Esm))
+          Some(value.unwrap_or(NodeModuleKind::Esm))
         }
       }
       MediaType::Wasm |
-      MediaType::Json => Some(ModuleKind::Esm),
+      MediaType::Json => Some(NodeModuleKind::Esm),
       MediaType::JavaScript
       | MediaType::Jsx
       | MediaType::TypeScript
@@ -534,18 +608,18 @@ impl CjsTracker {
       | MediaType::Css
       | MediaType::SourceMap
       | MediaType::Unknown => {
-        if let Some(value) = self.known.get(specifier).map(|v| *v) {
-          if value.is_cjs() && is_script == Some(false) {
+        if let Some(value) = known_cache.get(specifier).map(|v| *v) {
+          if value == NodeModuleKind::Cjs && is_script == Some(false) {
             // we now know this is actually esm
-            self.known.insert(specifier.clone(), ModuleKind::Esm);
-            Some(ModuleKind::Esm)
+            known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
+            Some(NodeModuleKind::Esm)
           } else {
             Some(value)
           }
         } else if is_script == Some(false) {
           // we know this is esm
-          self.known.insert(specifier.clone(), ModuleKind::Esm);
-          Some(ModuleKind::Esm)
+            known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
+          Some(NodeModuleKind::Esm)
         } else {
           None
         }
@@ -556,27 +630,38 @@ impl CjsTracker {
   fn check_based_on_pkg_json(
     &self,
     specifier: &ModuleSpecifier,
-  ) -> Result<ModuleKind, ClosestPkgJsonError> {
+  ) -> Result<NodeModuleKind, ClosestPkgJsonError> {
     if self.in_npm_pkg_checker.in_npm_package(specifier) {
       if let Some(pkg_json) =
         self.pkg_json_resolver.get_closest_package_json(specifier)?
       {
         let is_file_location_cjs = pkg_json.typ != "module";
-        Ok(ModuleKind::from_is_cjs(is_file_location_cjs))
+        Ok(if is_file_location_cjs {
+          NodeModuleKind::Cjs
+        } else {
+          NodeModuleKind::Esm
+        })
       } else {
-        Ok(ModuleKind::Cjs)
+        Ok(NodeModuleKind::Cjs)
       }
-    } else if self.unstable_detect_cjs {
+    } else if self.options.detect_cjs || self.options.is_node_main {
       if let Some(pkg_json) =
         self.pkg_json_resolver.get_closest_package_json(specifier)?
       {
-        let is_cjs_type = pkg_json.typ == "commonjs";
-        Ok(ModuleKind::from_is_cjs(is_cjs_type))
+        let is_cjs_type = pkg_json.typ == "commonjs"
+          || self.options.is_node_main && pkg_json.typ == "none";
+        Ok(if is_cjs_type {
+          NodeModuleKind::Cjs
+        } else {
+          NodeModuleKind::Esm
+        })
+      } else if self.options.is_node_main {
+        Ok(NodeModuleKind::Cjs)
       } else {
-        Ok(ModuleKind::Esm)
+        Ok(NodeModuleKind::Esm)
       }
     } else {
-      Ok(ModuleKind::Esm)
+      Ok(NodeModuleKind::Esm)
     }
   }
 }
@@ -587,48 +672,33 @@ pub type CliSloppyImportsResolver =
 /// A resolver that takes care of resolution, taking into account loaded
 /// import map, JSX settings.
 #[derive(Debug)]
-pub struct CliGraphResolver {
+pub struct CliResolver {
   node_resolver: Option<Arc<CliNodeResolver>>,
   npm_resolver: Option<Arc<dyn CliNpmResolver>>,
   sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
   workspace_resolver: Arc<WorkspaceResolver>,
-  maybe_default_jsx_import_source: Option<String>,
-  maybe_default_jsx_import_source_types: Option<String>,
-  maybe_jsx_import_source_module: Option<String>,
   maybe_vendor_specifier: Option<ModuleSpecifier>,
   found_package_json_dep_flag: AtomicFlag,
   bare_node_builtins_enabled: bool,
   warned_pkgs: DashSet<PackageReq>,
 }
 
-pub struct CliGraphResolverOptions<'a> {
+pub struct CliResolverOptions<'a> {
   pub node_resolver: Option<Arc<CliNodeResolver>>,
   pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
   pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
   pub workspace_resolver: Arc<WorkspaceResolver>,
   pub bare_node_builtins_enabled: bool,
-  pub maybe_jsx_import_source_config: Option<JsxImportSourceConfig>,
   pub maybe_vendor_dir: Option<&'a PathBuf>,
 }
 
-impl CliGraphResolver {
-  pub fn new(options: CliGraphResolverOptions) -> Self {
+impl CliResolver {
+  pub fn new(options: CliResolverOptions) -> Self {
     Self {
       node_resolver: options.node_resolver,
       npm_resolver: options.npm_resolver,
       sloppy_imports_resolver: options.sloppy_imports_resolver,
       workspace_resolver: options.workspace_resolver,
-      maybe_default_jsx_import_source: options
-        .maybe_jsx_import_source_config
-        .as_ref()
-        .and_then(|c| c.default_specifier.clone()),
-      maybe_default_jsx_import_source_types: options
-        .maybe_jsx_import_source_config
-        .as_ref()
-        .and_then(|c| c.default_types_specifier.clone()),
-      maybe_jsx_import_source_module: options
-        .maybe_jsx_import_source_config
-        .map(|c| c.module),
       maybe_vendor_specifier: options
         .maybe_vendor_dir
         .and_then(|v| ModuleSpecifier::from_directory_path(v).ok()),
@@ -638,10 +708,6 @@ impl CliGraphResolver {
     }
   }
 
-  pub fn as_graph_resolver(&self) -> &dyn Resolver {
-    self
-  }
-
   pub fn create_graph_npm_resolver(&self) -> WorkerCliNpmGraphResolver {
     WorkerCliNpmGraphResolver {
       npm_resolver: self.npm_resolver.as_ref(),
@@ -649,28 +715,12 @@ impl CliGraphResolver {
       bare_node_builtins_enabled: self.bare_node_builtins_enabled,
     }
   }
-}
-
-impl Resolver for CliGraphResolver {
-  fn default_jsx_import_source(&self) -> Option<String> {
-    self.maybe_default_jsx_import_source.clone()
-  }
-
-  fn default_jsx_import_source_types(&self) -> Option<String> {
-    self.maybe_default_jsx_import_source_types.clone()
-  }
 
-  fn jsx_import_source_module(&self) -> &str {
-    self
-      .maybe_jsx_import_source_module
-      .as_deref()
-      .unwrap_or(DEFAULT_JSX_IMPORT_SOURCE_MODULE)
-  }
-
-  fn resolve(
+  pub fn resolve(
     &self,
     raw_specifier: &str,
     referrer_range: &deno_graph::Range,
+    referrer_kind: NodeModuleKind,
     mode: ResolutionMode,
   ) -> Result<ModuleSpecifier, ResolveError> {
     fn to_node_mode(mode: ResolutionMode) -> NodeResolutionMode {
@@ -686,7 +736,7 @@ impl Resolver for CliGraphResolver {
     if let Some(node_resolver) = self.node_resolver.as_ref() {
       if referrer.scheme() == "file" && node_resolver.in_npm_package(referrer) {
         return node_resolver
-          .resolve(raw_specifier, referrer, to_node_mode(mode))
+          .resolve(raw_specifier, referrer, referrer_kind, to_node_mode(mode))
           .map(|res| res.into_url())
           .map_err(|e| ResolveError::Other(e.into()));
       }
@@ -759,6 +809,7 @@ impl Resolver for CliGraphResolver {
             pkg_json.dir_path(),
             sub_path.as_deref(),
             Some(referrer),
+            referrer_kind,
             to_node_mode(mode),
           )
           .map_err(|e| ResolveError::Other(e.into())),
@@ -800,6 +851,7 @@ impl Resolver for CliGraphResolver {
                       pkg_folder,
                       sub_path.as_deref(),
                       Some(referrer),
+                      referrer_kind,
                       to_node_mode(mode),
                     )
                     .map_err(|e| ResolveError::Other(e.into()))
@@ -847,6 +899,7 @@ impl Resolver for CliGraphResolver {
                 pkg_folder,
                 npm_req_ref.sub_path(),
                 Some(referrer),
+                referrer_kind,
                 to_node_mode(mode),
               )
               .map_err(|e| ResolveError::Other(e.into()));
@@ -855,7 +908,12 @@ impl Resolver for CliGraphResolver {
           // do npm resolution for byonm
           if is_byonm {
             return node_resolver
-              .resolve_req_reference(&npm_req_ref, referrer, to_node_mode(mode))
+              .resolve_req_reference(
+                &npm_req_ref,
+                referrer,
+                referrer_kind,
+                to_node_mode(mode),
+              )
               .map_err(|err| err.into());
           }
         }
@@ -869,7 +927,12 @@ impl Resolver for CliGraphResolver {
         // If byonm, check if the bare specifier resolves to an npm package
         if is_byonm && referrer.scheme() == "file" {
           let maybe_resolution = node_resolver
-            .resolve_if_for_npm_pkg(raw_specifier, referrer, to_node_mode(mode))
+            .resolve_if_for_npm_pkg(
+              raw_specifier,
+              referrer,
+              referrer_kind,
+              to_node_mode(mode),
+            )
             .map_err(ResolveError::Other)?;
           if let Some(res) = maybe_resolution {
             match res {
diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json
index 27c8499ea21108..ed80eb17b12205 100644
--- a/cli/schemas/config-file.v1.json
+++ b/cli/schemas/config-file.v1.json
@@ -528,7 +528,6 @@
           "bare-node-builtins",
           "byonm",
           "cron",
-          "detect-cjs",
           "ffi",
           "fs",
           "fmt-component",
diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs
index 960aad15784a1a..b48e1c97ceb63f 100644
--- a/cli/standalone/binary.rs
+++ b/cli/standalone/binary.rs
@@ -720,7 +720,6 @@ impl<'a> DenoCompileBinaryWriter<'a> {
       unstable_config: UnstableConfig {
         legacy_flag_enabled: false,
         bare_node_builtins: cli_options.unstable_bare_node_builtins(),
-        detect_cjs: cli_options.unstable_detect_cjs(),
         sloppy_imports: cli_options.unstable_sloppy_imports(),
         features: cli_options.unstable_features(),
       },
diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs
index bb0ab423ddbf84..15937c7aee7608 100644
--- a/cli/standalone/mod.rs
+++ b/cli/standalone/mod.rs
@@ -45,6 +45,8 @@ use deno_runtime::WorkerLogLevel;
 use deno_semver::npm::NpmPackageReqReference;
 use import_map::parse_from_json;
 use node_resolver::analyze::NodeCodeTranslator;
+use node_resolver::errors::ClosestPkgJsonError;
+use node_resolver::NodeModuleKind;
 use node_resolver::NodeResolutionMode;
 use serialization::DenoCompileModuleSource;
 use std::borrow::Cow;
@@ -76,9 +78,9 @@ use crate::npm::CliNpmResolverCreateOptions;
 use crate::npm::CliNpmResolverManagedSnapshotOption;
 use crate::npm::CreateInNpmPkgCheckerOptions;
 use crate::resolver::CjsTracker;
-use crate::resolver::CjsTrackerOptions;
 use crate::resolver::CliDenoResolverFs;
 use crate::resolver::CliNodeResolver;
+use crate::resolver::IsCjsResolverOptions;
 use crate::resolver::NpmModuleLoader;
 use crate::util::progress_bar::ProgressBar;
 use crate::util::progress_bar::ProgressBarStyle;
@@ -146,13 +148,27 @@ impl ModuleLoader for EmbeddedModuleLoader {
         type_error(format!("Referrer uses invalid specifier: {}", err))
       })?
     };
+    let referrer_kind = if self
+      .shared
+      .cjs_tracker
+      .is_maybe_cjs(&referrer, MediaType::from_specifier(&referrer))?
+    {
+      NodeModuleKind::Cjs
+    } else {
+      NodeModuleKind::Esm
+    };
 
     if self.shared.node_resolver.in_npm_package(&referrer) {
       return Ok(
         self
           .shared
           .node_resolver
-          .resolve(raw_specifier, &referrer, NodeResolutionMode::Execution)?
+          .resolve(
+            raw_specifier,
+            &referrer,
+            referrer_kind,
+            NodeResolutionMode::Execution,
+          )?
           .into_url(),
       );
     }
@@ -178,6 +194,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
             pkg_json.dir_path(),
             sub_path.as_deref(),
             Some(&referrer),
+            referrer_kind,
             NodeResolutionMode::Execution,
           )?,
       ),
@@ -192,6 +209,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
             req,
             sub_path.as_deref(),
             &referrer,
+            referrer_kind,
             NodeResolutionMode::Execution,
           )
         }
@@ -211,6 +229,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
                 pkg_folder,
                 sub_path.as_deref(),
                 Some(&referrer),
+                referrer_kind,
                 NodeResolutionMode::Execution,
               )?,
           )
@@ -224,6 +243,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
           return self.shared.node_resolver.resolve_req_reference(
             &reference,
             &referrer,
+            referrer_kind,
             NodeResolutionMode::Execution,
           );
         }
@@ -250,6 +270,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
         let maybe_res = self.shared.node_resolver.resolve_if_for_npm_pkg(
           raw_specifier,
           &referrer,
+          referrer_kind,
           NodeResolutionMode::Execution,
         )?;
         if let Some(res) = maybe_res {
@@ -429,6 +450,14 @@ impl NodeRequireLoader for EmbeddedModuleLoader {
   ) -> Result<String, AnyError> {
     Ok(self.shared.fs.read_text_file_lossy_sync(path, None)?)
   }
+
+  fn is_maybe_cjs(
+    &self,
+    specifier: &ModuleSpecifier,
+  ) -> Result<bool, ClosestPkgJsonError> {
+    let media_type = MediaType::from_specifier(specifier);
+    self.shared.cjs_tracker.is_maybe_cjs(specifier, media_type)
+  }
 }
 
 struct StandaloneModuleLoaderFactory {
@@ -628,14 +657,14 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
   let cjs_tracker = Arc::new(CjsTracker::new(
     in_npm_pkg_checker.clone(),
     pkg_json_resolver.clone(),
-    CjsTrackerOptions {
-      unstable_detect_cjs: metadata.unstable_config.detect_cjs,
+    IsCjsResolverOptions {
+      detect_cjs: !metadata.workspace_resolver.package_jsons.is_empty(),
+      is_node_main: false,
     },
   ));
   let cache_db = Caches::new(deno_dir_provider.clone());
   let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db());
   let cli_node_resolver = Arc::new(CliNodeResolver::new(
-    cjs_tracker.clone(),
     fs.clone(),
     in_npm_pkg_checker.clone(),
     node_resolver.clone(),
@@ -646,7 +675,6 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
     cjs_tracker.clone(),
     fs.clone(),
     None,
-    false,
   );
   let node_code_translator = Arc::new(NodeCodeTranslator::new(
     cjs_esm_code_analyzer,
diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs
index f59333247510fc..2a554c13359ad2 100644
--- a/cli/tools/coverage/mod.rs
+++ b/cli/tools/coverage/mod.rs
@@ -480,7 +480,7 @@ fn filter_coverages(
     .filter(|e| {
       let is_internal = e.url.starts_with("ext:")
         || e.url.ends_with("__anonymous__")
-        || e.url.ends_with("$deno$test.js")
+        || e.url.ends_with("$deno$test.mjs")
         || e.url.ends_with(".snap")
         || is_supported_test_path(Path::new(e.url.as_str()))
         || doc_test_re.is_match(e.url.as_str())
diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs
index ed86e86c79bf0f..1655f0a322eaff 100644
--- a/cli/tools/installer.rs
+++ b/cli/tools/installer.rs
@@ -1396,6 +1396,7 @@ mod tests {
       .env_clear()
       // use the deno binary in the target directory
       .env("PATH", test_util::target_dir())
+      .env("RUST_BACKTRACE", "1")
       .spawn()
       .unwrap()
       .wait()
diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs
index 0ffd0da1ee1187..732f95c49f25f7 100644
--- a/cli/tools/jupyter/mod.rs
+++ b/cli/tools/jupyter/mod.rs
@@ -61,7 +61,7 @@ pub async fn kernel(
   let factory = CliFactory::from_flags(flags);
   let cli_options = factory.cli_options()?;
   let main_module =
-    resolve_url_or_path("./$deno$jupyter.ts", cli_options.initial_cwd())
+    resolve_url_or_path("./$deno$jupyter.mts", cli_options.initial_cwd())
       .unwrap();
   // TODO(bartlomieju): should we run with all permissions?
   let permissions =
diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs
index e096b486ef2b2f..d8edf240481cf5 100644
--- a/cli/tools/lint/mod.rs
+++ b/cli/tools/lint/mod.rs
@@ -63,7 +63,7 @@ pub use rules::LintRuleProvider;
 
 const JSON_SCHEMA_VERSION: u8 = 1;
 
-static STDIN_FILE_NAME: &str = "$deno$stdin.ts";
+static STDIN_FILE_NAME: &str = "$deno$stdin.mts";
 
 pub async fn lint(
   flags: Arc<Flags>,
diff --git a/cli/tools/lint/rules/no_sloppy_imports.rs b/cli/tools/lint/rules/no_sloppy_imports.rs
index 2f60875885fc0d..94bf9a7c676685 100644
--- a/cli/tools/lint/rules/no_sloppy_imports.rs
+++ b/cli/tools/lint/rules/no_sloppy_imports.rs
@@ -87,6 +87,7 @@ impl LintRule for NoSloppyImportsRule {
       captures: Default::default(),
     };
 
+    // fill this and capture the sloppy imports in the resolver
     deno_graph::parse_module_from_ast(deno_graph::ParseModuleFromAstOptions {
       graph_kind: deno_graph::GraphKind::All,
       specifier: context.specifier().clone(),
diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs
index 23b0f11ac5266d..8e05c4abbcff89 100644
--- a/cli/tools/repl/session.rs
+++ b/cli/tools/repl/session.rs
@@ -7,7 +7,7 @@ use crate::cdp;
 use crate::colors;
 use crate::lsp::ReplLanguageServer;
 use crate::npm::CliNpmResolver;
-use crate::resolver::CliGraphResolver;
+use crate::resolver::CliResolver;
 use crate::tools::test::report_tests;
 use crate::tools::test::reporters::PrettyTestReporter;
 use crate::tools::test::reporters::TestReporter;
@@ -44,12 +44,12 @@ use deno_core::url::Url;
 use deno_core::LocalInspectorSession;
 use deno_core::PollEventLoopOptions;
 use deno_graph::source::ResolutionMode;
-use deno_graph::source::Resolver;
 use deno_graph::Position;
 use deno_graph::PositionRange;
 use deno_graph::SpecifierWithRange;
 use deno_runtime::worker::MainWorker;
 use deno_semver::npm::NpmPackageReqReference;
+use node_resolver::NodeModuleKind;
 use once_cell::sync::Lazy;
 use regex::Match;
 use regex::Regex;
@@ -180,7 +180,7 @@ struct ReplJsxState {
 
 pub struct ReplSession {
   npm_resolver: Arc<dyn CliNpmResolver>,
-  resolver: Arc<CliGraphResolver>,
+  resolver: Arc<CliResolver>,
   pub worker: MainWorker,
   session: LocalInspectorSession,
   pub context_id: u64,
@@ -199,7 +199,7 @@ impl ReplSession {
   pub async fn initialize(
     cli_options: &CliOptions,
     npm_resolver: Arc<dyn CliNpmResolver>,
-    resolver: Arc<CliGraphResolver>,
+    resolver: Arc<CliResolver>,
     mut worker: MainWorker,
     main_module: ModuleSpecifier,
     test_event_receiver: TestEventReceiver,
@@ -245,7 +245,7 @@ impl ReplSession {
     assert_ne!(context_id, 0);
 
     let referrer =
-      deno_core::resolve_path("./$deno$repl.ts", cli_options.initial_cwd())
+      deno_core::resolve_path("./$deno$repl.mts", cli_options.initial_cwd())
         .unwrap();
 
     let cwd_url =
@@ -712,7 +712,12 @@ impl ReplSession {
       .flat_map(|i| {
         self
           .resolver
-          .resolve(i, &referrer_range, ResolutionMode::Execution)
+          .resolve(
+            i,
+            &referrer_range,
+            NodeModuleKind::Esm,
+            ResolutionMode::Execution,
+          )
           .ok()
           .or_else(|| ModuleSpecifier::parse(i).ok())
       })
diff --git a/cli/tools/run/hmr.rs b/cli/tools/run/hmr.rs
index 6cebedd0127c5f..373c207d6991ee 100644
--- a/cli/tools/run/hmr.rs
+++ b/cli/tools/run/hmr.rs
@@ -4,8 +4,6 @@ use std::collections::HashMap;
 use std::path::PathBuf;
 use std::sync::Arc;
 
-use deno_ast::MediaType;
-use deno_ast::ModuleKind;
 use deno_core::error::generic_error;
 use deno_core::error::AnyError;
 use deno_core::futures::StreamExt;
@@ -18,7 +16,6 @@ use tokio::select;
 
 use crate::cdp;
 use crate::emit::Emitter;
-use crate::resolver::CjsTracker;
 use crate::util::file_watcher::WatcherCommunicator;
 use crate::util::file_watcher::WatcherRestartMode;
 
@@ -63,7 +60,6 @@ pub struct HmrRunner {
   session: LocalInspectorSession,
   watcher_communicator: Arc<WatcherCommunicator>,
   script_ids: HashMap<String, String>,
-  cjs_tracker: Arc<CjsTracker>,
   emitter: Arc<Emitter>,
 }
 
@@ -146,7 +142,6 @@ impl crate::worker::HmrRunner for HmrRunner {
 
             let source_code = self.emitter.load_and_emit_for_hmr(
               &module_url,
-              ModuleKind::from_is_cjs(self.cjs_tracker.is_maybe_cjs(&module_url, MediaType::from_specifier(&module_url))?),
             ).await?;
 
             let mut tries = 1;
@@ -179,14 +174,12 @@ impl crate::worker::HmrRunner for HmrRunner {
 
 impl HmrRunner {
   pub fn new(
-    cjs_tracker: Arc<CjsTracker>,
     emitter: Arc<Emitter>,
     session: LocalInspectorSession,
     watcher_communicator: Arc<WatcherCommunicator>,
   ) -> Self {
     Self {
       session,
-      cjs_tracker,
       emitter,
       watcher_communicator,
       script_ids: HashMap::new(),
diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js
index 52c9134dad871c..68d099253a5f4e 100644
--- a/cli/tsc/99_main_compiler.js
+++ b/cli/tsc/99_main_compiler.js
@@ -121,8 +121,8 @@ delete Object.prototype.__proto__;
   /** @type {Map<string, ts.SourceFile>} */
   const sourceFileCache = new Map();
 
-  /** @type {Map<string, string>} */
-  const sourceTextCache = new Map();
+  /** @type {Map<string, ts.IScriptSnapshot & { isCjs?: boolean; }>} */
+  const scriptSnapshotCache = new Map();
 
   /** @type {Map<string, number>} */
   const sourceRefCounts = new Map();
@@ -133,9 +133,6 @@ delete Object.prototype.__proto__;
   /** @type {Map<string, boolean>} */
   const isNodeSourceFileCache = new Map();
 
-  /** @type {Map<string, boolean>} */
-  const isCjsCache = new Map();
-
   // Maps asset specifiers to the first scope that the asset was loaded into.
   /** @type {Map<string, string | null>} */
   const assetScopes = new Map();
@@ -210,12 +207,13 @@ delete Object.prototype.__proto__;
       const mapKey = path + key;
       let sourceFile = documentRegistrySourceFileCache.get(mapKey);
       if (!sourceFile || sourceFile.version !== version) {
+        const isCjs = /** @type {any} */ (scriptSnapshot).isCjs;
         sourceFile = ts.createLanguageServiceSourceFile(
           fileName,
           scriptSnapshot,
           {
             ...getCreateSourceFileOptions(sourceFileOptions),
-            impliedNodeFormat: (isCjsCache.get(fileName) ?? false)
+            impliedNodeFormat: isCjs
               ? ts.ModuleKind.CommonJS
               : ts.ModuleKind.ESNext,
             // in the lsp we want to be able to show documentation
@@ -320,7 +318,7 @@ delete Object.prototype.__proto__;
         if (lastRequestMethod != "cleanupSemanticCache") {
           const mapKey = path + key;
           documentRegistrySourceFileCache.delete(mapKey);
-          sourceTextCache.delete(path);
+          scriptSnapshotCache.delete(path);
           ops.op_release(path);
         }
       } else {
@@ -624,8 +622,6 @@ delete Object.prototype.__proto__;
         `"data" is unexpectedly null for "${specifier}".`,
       );
 
-      isCjsCache.set(specifier, isCjs);
-
       sourceFile = ts.createSourceFile(
         specifier,
         data,
@@ -699,7 +695,7 @@ delete Object.prototype.__proto__;
           /** @type {[string, ts.Extension] | undefined} */
           const resolved = ops.op_resolve(
             containingFilePath,
-            isCjsCache.get(containingFilePath) ?? false,
+            containingFileMode === ts.ModuleKind.CommonJS,
             [fileReference.fileName],
           )?.[0];
           if (resolved) {
@@ -723,7 +719,14 @@ delete Object.prototype.__proto__;
         }
       });
     },
-    resolveModuleNames(specifiers, base) {
+    resolveModuleNames(
+      specifiers,
+      base,
+      _reusedNames,
+      _redirectedReference,
+      _options,
+      containingSourceFile,
+    ) {
       if (logDebug) {
         debug(`host.resolveModuleNames()`);
         debug(`  base: ${base}`);
@@ -732,7 +735,7 @@ delete Object.prototype.__proto__;
       /** @type {Array<[string, ts.Extension] | undefined>} */
       const resolved = ops.op_resolve(
         base,
-        isCjsCache.get(base) ?? false,
+        containingSourceFile?.impliedNodeFormat === ts.ModuleKind.CommonJS,
         specifiers,
       );
       if (resolved) {
@@ -814,19 +817,19 @@ delete Object.prototype.__proto__;
           return ts.ScriptSnapshot.fromString(sourceFile.text);
         }
       }
-      let sourceText = sourceTextCache.get(specifier);
-      if (sourceText == undefined) {
+      let scriptSnapshot = scriptSnapshotCache.get(specifier);
+      if (scriptSnapshot == undefined) {
         /** @type {{ data: string, version: string, isCjs: boolean }} */
         const fileInfo = ops.op_load(specifier);
         if (!fileInfo) {
           return undefined;
         }
-        isCjsCache.set(specifier, fileInfo.isCjs);
-        sourceTextCache.set(specifier, fileInfo.data);
+        scriptSnapshot = ts.ScriptSnapshot.fromString(fileInfo.data);
+        scriptSnapshot.isCjs = fileInfo.isCjs;
+        scriptSnapshotCache.set(specifier, scriptSnapshot);
         scriptVersionCache.set(specifier, fileInfo.version);
-        sourceText = fileInfo.data;
       }
-      return ts.ScriptSnapshot.fromString(sourceText);
+      return scriptSnapshot;
     },
   };
 
@@ -1238,7 +1241,7 @@ delete Object.prototype.__proto__;
           closed = true;
         }
         scriptVersionCache.delete(script);
-        sourceTextCache.delete(script);
+        scriptSnapshotCache.delete(script);
       }
 
       if (newConfigsByScope || opened || closed) {
diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs
index dc7fc38f7a0172..a569061625a7a5 100644
--- a/cli/tsc/mod.rs
+++ b/cli/tsc/mod.rs
@@ -343,31 +343,36 @@ impl TypeCheckingCjsTracker {
     media_type: MediaType,
     code: &Arc<str>,
   ) -> bool {
-    if let Some(module_kind) =
-      self.cjs_tracker.get_known_kind(specifier, media_type)
-    {
-      module_kind.is_cjs()
-    } else {
-      let maybe_is_script = self
-        .module_info_cache
-        .as_module_analyzer()
-        .analyze_sync(specifier, media_type, code)
-        .ok()
-        .map(|info| info.is_script);
-      maybe_is_script
-        .and_then(|is_script| {
-          self
-            .cjs_tracker
-            .is_cjs_with_known_is_script(specifier, media_type, is_script)
-            .ok()
-        })
-        .unwrap_or_else(|| {
-          self
-            .cjs_tracker
-            .is_maybe_cjs(specifier, media_type)
-            .unwrap_or(false)
-        })
-    }
+    let maybe_is_script = self
+      .module_info_cache
+      .as_module_analyzer()
+      .analyze_sync(specifier, media_type, code)
+      .ok()
+      .map(|info| info.is_script);
+    maybe_is_script
+      .and_then(|is_script| {
+        self
+          .cjs_tracker
+          .is_cjs_with_known_is_script(specifier, media_type, is_script)
+          .ok()
+      })
+      .unwrap_or_else(|| {
+        self
+          .cjs_tracker
+          .is_maybe_cjs(specifier, media_type)
+          .unwrap_or(false)
+      })
+  }
+
+  pub fn is_cjs_with_known_is_script(
+    &self,
+    specifier: &ModuleSpecifier,
+    media_type: MediaType,
+    is_script: bool,
+  ) -> Result<bool, node_resolver::errors::ClosestPkgJsonError> {
+    self
+      .cjs_tracker
+      .is_cjs_with_known_is_script(specifier, media_type, is_script)
   }
 }
 
@@ -627,8 +632,12 @@ fn op_load_inner(
       match module {
         Module::Js(module) => {
           media_type = module.media_type;
-          if matches!(media_type, MediaType::Cjs | MediaType::Cts) {
-            is_cjs = true;
+          if let Some(npm_state) = &state.maybe_npm {
+            is_cjs = npm_state.cjs_tracker.is_cjs_with_known_is_script(
+              specifier,
+              module.media_type,
+              module.is_script,
+            )?;
           }
           let source = module
             .fast_check_module()
@@ -737,6 +746,7 @@ fn op_resolve_inner(
       "Error converting a string module specifier for \"op_resolve\".",
     )?
   };
+  let referrer_module = state.graph.get(&referrer);
   for specifier in args.specifiers {
     if specifier.starts_with("node:") {
       resolved.push((
@@ -752,16 +762,19 @@ fn op_resolve_inner(
       continue;
     }
 
-    let graph = &state.graph;
-    let resolved_dep = graph
-      .get(&referrer)
+    let resolved_dep = referrer_module
       .and_then(|m| m.js())
       .and_then(|m| m.dependencies_prefer_fast_check().get(&specifier))
       .and_then(|d| d.maybe_type.ok().or_else(|| d.maybe_code.ok()));
 
     let maybe_result = match resolved_dep {
       Some(ResolutionResolved { specifier, .. }) => {
-        resolve_graph_specifier_types(specifier, &referrer, state)?
+        resolve_graph_specifier_types(
+          specifier,
+          &referrer,
+          referrer_kind,
+          state,
+        )?
       }
       _ => {
         match resolve_non_graph_specifier_types(
@@ -834,6 +847,7 @@ fn op_resolve_inner(
 fn resolve_graph_specifier_types(
   specifier: &ModuleSpecifier,
   referrer: &ModuleSpecifier,
+  referrer_kind: NodeModuleKind,
   state: &State,
 ) -> Result<Option<(ModuleSpecifier, MediaType)>, AnyError> {
   let graph = &state.graph;
@@ -886,6 +900,7 @@ fn resolve_graph_specifier_types(
             &package_folder,
             module.nv_reference.sub_path(),
             Some(referrer),
+            referrer_kind,
             NodeResolutionMode::Types,
           );
         let maybe_url = match res_result {
@@ -965,6 +980,7 @@ fn resolve_non_graph_specifier_types(
       &package_folder,
       npm_req_ref.sub_path(),
       Some(referrer),
+      referrer_kind,
       NodeResolutionMode::Types,
     );
     let maybe_url = match res_result {
diff --git a/cli/worker.rs b/cli/worker.rs
index 402644a42cf17e..83e36b36c218a4 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -44,6 +44,7 @@ use deno_runtime::WorkerExecutionMode;
 use deno_runtime::WorkerLogLevel;
 use deno_semver::npm::NpmPackageReqReference;
 use deno_terminal::colors;
+use node_resolver::NodeModuleKind;
 use node_resolver::NodeResolutionMode;
 use tokio::select;
 
@@ -680,6 +681,7 @@ impl CliMainWorkerFactory {
         package_folder,
         sub_path,
         /* referrer */ None,
+        NodeModuleKind::Esm,
         NodeResolutionMode::Execution,
       )?;
     if specifier
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 9ca21e9941a162..7ddae9e0803f06 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -14,6 +14,7 @@ use deno_core::url::Url;
 #[allow(unused_imports)]
 use deno_core::v8;
 use deno_core::v8::ExternalReference;
+use node_resolver::errors::ClosestPkgJsonError;
 use node_resolver::NpmResolverRc;
 use once_cell::sync::Lazy;
 
@@ -157,6 +158,10 @@ pub trait NodeRequireLoader {
   ) -> Result<Cow<'a, Path>, AnyError>;
 
   fn load_text_file_lossy(&self, path: &Path) -> Result<String, AnyError>;
+
+  /// Get if the module kind is maybe CJS and loading should determine
+  /// if its CJS or ESM.
+  fn is_maybe_cjs(&self, specifier: &Url) -> Result<bool, ClosestPkgJsonError>;
 }
 
 pub static NODE_ENV_VAR_ALLOWLIST: Lazy<HashSet<String>> = Lazy::new(|| {
@@ -384,6 +389,7 @@ deno_core::extension!(deno_node,
     ops::require::op_require_proxy_path,
     ops::require::op_require_is_deno_dir_package,
     ops::require::op_require_resolve_deno_dir,
+    ops::require::op_require_is_maybe_cjs,
     ops::require::op_require_is_request_relative,
     ops::require::op_require_resolve_lookup_paths,
     ops::require::op_require_try_self_parent_path<P>,
@@ -397,7 +403,6 @@ deno_core::extension!(deno_node,
     ops::require::op_require_read_file<P>,
     ops::require::op_require_as_file_path,
     ops::require::op_require_resolve_exports<P>,
-    ops::require::op_require_read_closest_package_json<P>,
     ops::require::op_require_read_package_scope<P>,
     ops::require::op_require_package_imports_resolve<P>,
     ops::require::op_require_break_on_next_statement,
diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs
index 30db8b629325d8..b7fa8feb20145c 100644
--- a/ext/node/ops/require.rs
+++ b/ext/node/ops/require.rs
@@ -1,16 +1,18 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
 
+use deno_core::error::AnyError;
 use deno_core::op2;
 use deno_core::url::Url;
 use deno_core::v8;
 use deno_core::JsRuntimeInspector;
-use deno_core::ModuleSpecifier;
 use deno_core::OpState;
 use deno_fs::FileSystemRc;
+use deno_package_json::NodeModuleKind;
 use deno_package_json::PackageJsonRc;
 use deno_path_util::normalize_path;
+use deno_path_util::url_from_file_path;
 use deno_path_util::url_to_file_path;
-use node_resolver::NodeModuleKind;
+use node_resolver::errors::ClosestPkgJsonError;
 use node_resolver::NodeResolutionMode;
 use node_resolver::REQUIRE_CONDITIONS;
 use std::borrow::Cow;
@@ -217,17 +219,17 @@ pub fn op_require_resolve_deno_dir(
   state: &mut OpState,
   #[string] request: String,
   #[string] parent_filename: String,
-) -> Option<String> {
+) -> Result<Option<String>, AnyError> {
   let resolver = state.borrow::<NpmResolverRc>();
-  resolver
-    .resolve_package_folder_from_package(
-      &request,
-      &ModuleSpecifier::from_file_path(&parent_filename).unwrap_or_else(|_| {
-        panic!("Url::from_file_path: [{:?}]", parent_filename)
-      }),
-    )
-    .ok()
-    .map(|p| p.to_string_lossy().into_owned())
+  Ok(
+    resolver
+      .resolve_package_folder_from_package(
+        &request,
+        &url_from_file_path(&PathBuf::from(parent_filename))?,
+      )
+      .ok()
+      .map(|p| p.to_string_lossy().into_owned()),
+  )
 }
 
 #[op2(fast)]
@@ -564,19 +566,17 @@ where
   }))
 }
 
-#[op2]
-#[serde]
-pub fn op_require_read_closest_package_json<P>(
+#[op2(fast)]
+pub fn op_require_is_maybe_cjs(
   state: &mut OpState,
   #[string] filename: String,
-) -> Result<Option<PackageJsonRc>, node_resolver::errors::ClosestPkgJsonError>
-where
-  P: NodePermissions + 'static,
-{
+) -> Result<bool, ClosestPkgJsonError> {
   let filename = PathBuf::from(filename);
-  // permissions: allow reading the closest package.json files
-  let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
-  pkg_json_resolver.get_closest_package_json_from_path(&filename)
+  let Ok(url) = url_from_file_path(&filename) else {
+    return Ok(false);
+  };
+  let loader = state.borrow::<NodeRequireLoaderRc>();
+  loader.is_maybe_cjs(&url)
 }
 
 #[op2]
diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js
index 0d267ca444b825..083d4e49bef08c 100644
--- a/ext/node/polyfills/01_require.js
+++ b/ext/node/polyfills/01_require.js
@@ -11,6 +11,7 @@ import {
   op_require_can_parse_as_esm,
   op_require_init_paths,
   op_require_is_deno_dir_package,
+  op_require_is_maybe_cjs,
   op_require_is_request_relative,
   op_require_node_module_paths,
   op_require_package_imports_resolve,
@@ -19,7 +20,6 @@ import {
   op_require_path_is_absolute,
   op_require_path_resolve,
   op_require_proxy_path,
-  op_require_read_closest_package_json,
   op_require_read_file,
   op_require_read_package_scope,
   op_require_real_path,
@@ -1060,36 +1060,13 @@ Module.prototype._compile = function (content, filename, format) {
   return result;
 };
 
-Module._extensions[".js"] = function (module, filename) {
-  const content = op_require_read_file(filename);
-
-  let format;
-  if (StringPrototypeEndsWith(filename, ".js")) {
-    const pkg = op_require_read_closest_package_json(filename);
-    if (pkg?.type === "module") {
-      format = "module";
-    } else if (pkg?.type === "commonjs") {
-      format = "commonjs";
-    }
-  }
-
-  module._compile(content, filename, format);
-};
-
-Module._extensions[".ts"] =
+Module._extensions[".js"] =
+  Module._extensions[".ts"] =
   Module._extensions[".jsx"] =
   Module._extensions[".tsx"] =
     function (module, filename) {
       const content = op_require_read_file(filename);
-
-      let format;
-      const pkg = op_require_read_closest_package_json(filename);
-      if (pkg?.type === "module") {
-        format = "module";
-      } else if (pkg?.type === "commonjs") {
-        format = "commonjs";
-      }
-
+      const format = op_require_is_maybe_cjs(filename) ? undefined : "module";
       module._compile(content, filename, format);
     };
 
diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts
index bf626e410056bc..647376d5cf0147 100644
--- a/ext/node/polyfills/process.ts
+++ b/ext/node/polyfills/process.ts
@@ -919,7 +919,7 @@ Object.defineProperty(argv, "1", {
     if (Deno.mainModule?.startsWith("file:")) {
       return pathFromURL(new URL(Deno.mainModule));
     } else {
-      return join(Deno.cwd(), "$deno$node.js");
+      return join(Deno.cwd(), "$deno$node.mjs");
     }
   },
 });
diff --git a/resolvers/node/package_json.rs b/resolvers/node/package_json.rs
index 6967779e5d9113..ae016ebe3ec3c8 100644
--- a/resolvers/node/package_json.rs
+++ b/resolvers/node/package_json.rs
@@ -15,8 +15,8 @@ use crate::errors::CanonicalizingPkgJsonDirError;
 use crate::errors::ClosestPkgJsonError;
 use crate::errors::PackageJsonLoadError;
 
-// todo(dsherret): this isn't exactly correct and we should change it to instead
-// be created per worker and passed down as a ctor arg to the pkg json resolver
+// it would be nice if this was passed down as a ctor arg to the package.json resolver,
+// but it's a little bit complicated to do that, so we just maintain a thread local cache
 thread_local! {
   static CACHE: RefCell<HashMap<PathBuf, PackageJsonRc>> = RefCell::new(HashMap::new());
 }
diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs
index d44539e9786ad9..fcff2924250634 100644
--- a/resolvers/node/resolution.rs
+++ b/resolvers/node/resolution.rs
@@ -50,6 +50,15 @@ pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
 pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"];
 static TYPES_ONLY_CONDITIONS: &[&str] = &["types"];
 
+fn conditions_from_module_kind(
+  kind: NodeModuleKind,
+) -> &'static [&'static str] {
+  match kind {
+    NodeModuleKind::Esm => DEFAULT_CONDITIONS,
+    NodeModuleKind::Cjs => REQUIRE_CONDITIONS,
+  }
+}
+
 pub type NodeModuleKind = deno_package_json::NodeModuleKind;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -166,8 +175,7 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
       specifier,
       referrer,
       referrer_kind,
-      // even though the referrer may be CJS, if we're here that means we're doing ESM resolution
-      DEFAULT_CONDITIONS,
+      conditions_from_module_kind(referrer_kind),
       mode,
     )?;
 
@@ -299,9 +307,9 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
     package_dir: &Path,
     package_subpath: Option<&str>,
     maybe_referrer: Option<&Url>,
+    referrer_kind: NodeModuleKind,
     mode: NodeResolutionMode,
   ) -> Result<Url, PackageSubpathResolveError> {
-    let node_module_kind = NodeModuleKind::Esm;
     let package_subpath = package_subpath
       .map(|s| format!("./{s}"))
       .unwrap_or_else(|| ".".to_string());
@@ -309,8 +317,8 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
       package_dir,
       &package_subpath,
       maybe_referrer,
-      node_module_kind,
-      DEFAULT_CONDITIONS,
+      referrer_kind,
+      conditions_from_module_kind(referrer_kind),
       mode,
     )?;
     // TODO(bartlomieju): skipped checking errors for commonJS resolution and
@@ -441,10 +449,7 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
         /* sub path */ ".",
         maybe_referrer,
         referrer_kind,
-        match referrer_kind {
-          NodeModuleKind::Esm => DEFAULT_CONDITIONS,
-          NodeModuleKind::Cjs => REQUIRE_CONDITIONS,
-        },
+        conditions_from_module_kind(referrer_kind),
         NodeResolutionMode::Types,
       );
       if let Ok(resolution) = resolution_result {
diff --git a/runtime/fmt_errors.rs b/runtime/fmt_errors.rs
index 4cd8a063450e96..28cd702966225d 100644
--- a/runtime/fmt_errors.rs
+++ b/runtime/fmt_errors.rs
@@ -310,14 +310,13 @@ fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec<FixSuggestion> {
     {
       return vec![
         FixSuggestion::info_multiline(&[
-          cstr!("Deno supports CommonJS modules in <u>.cjs</> files, or when there's a <u>package.json</>"),
-          cstr!("with <i>\"type\": \"commonjs\"</> option and <i>--unstable-detect-cjs</> flag is used.")
+          cstr!("Deno supports CommonJS modules in <u>.cjs</> files, or when the closest"),
+          cstr!("<u>package.json</> has a <i>\"type\": \"commonjs\"</> option.")
         ]),
         FixSuggestion::hint_multiline(&[
           "Rewrite this module to ESM,",
           cstr!("or change the file extension to <u>.cjs</u>,"),
-          cstr!("or add <u>package.json</> next to the file with <i>\"type\": \"commonjs\"</> option"),
-          cstr!("and pass <i>--unstable-detect-cjs</> flag."),
+          cstr!("or add <u>package.json</> next to the file with <i>\"type\": \"commonjs\"</> option."),
         ]),
         FixSuggestion::docs("https://docs.deno.com/go/commonjs"),
       ];
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index d81c82c5013be7..e272291538cd7c 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -393,6 +393,13 @@ pub struct WebWorker {
   maybe_worker_metadata: Option<WorkerMetadata>,
 }
 
+impl Drop for WebWorker {
+  fn drop(&mut self) {
+    // clean up the package.json thread local cache
+    node_resolver::PackageJsonThreadLocalCache::clear();
+  }
+}
+
 impl WebWorker {
   pub fn bootstrap_from_options(
     services: WebWorkerServiceOptions,
diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs
index af5f9de23eb5e1..dcef6960848c95 100644
--- a/tests/integration/lsp_tests.rs
+++ b/tests/integration/lsp_tests.rs
@@ -16139,6 +16139,55 @@ fn lsp_cjs_import_dual() {
   );
 }
 
+#[test]
+fn lsp_type_commonjs() {
+  let context = TestContextBuilder::new()
+    .use_http_server()
+    .use_temp_cwd()
+    .add_npm_env_vars()
+    .build();
+  let temp_dir = context.temp_dir();
+  temp_dir.write("deno.json", r#"{}"#);
+  temp_dir.write(
+    "package.json",
+    r#"{
+  "type": "commonjs",
+  "dependencies": {
+    "@denotest/dual-cjs-esm": "1"
+  }
+}"#,
+  );
+  context.run_npm("install");
+
+  let mut client = context.new_lsp_command().build();
+  client.initialize_default();
+  let main_url = temp_dir.path().join("main.ts").url_file();
+  let diagnostics = client.did_open(
+    json!({
+      "textDocument": {
+        "uri": main_url,
+        "languageId": "typescript",
+        "version": 1,
+        // getKind() should resolve as "cjs" and cause a type checker error
+        "text": "import mod = require('@denotest/dual-cjs-esm');\nconst kind: 'other' = mod.getKind(); console.log(kind);",
+      }
+    }),
+  );
+  assert_eq!(
+    json!(diagnostics.all()),
+    json!([{
+      "range": {
+          "start": { "line": 1, "character": 6, },
+          "end": { "line": 1, "character": 10, },
+      },
+      "severity": 1,
+      "code": 2322,
+      "source": "deno-ts",
+      "message": "Type '\"cjs\"' is not assignable to type '\"other\"'.",
+    }])
+  );
+}
+
 #[test]
 fn lsp_ts_code_fix_any_param() {
   let context = TestContextBuilder::new().use_temp_cwd().build();
diff --git a/tests/node_compat/package.json b/tests/node_compat/package.json
new file mode 100644
index 00000000000000..5bbefffbabee39
--- /dev/null
+++ b/tests/node_compat/package.json
@@ -0,0 +1,3 @@
+{
+  "type": "commonjs"
+}
diff --git a/tests/node_compat/test/common/package.json b/tests/node_compat/test/common/package.json
deleted file mode 100644
index 0967ef424bce67..00000000000000
--- a/tests/node_compat/test/common/package.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/tests/node_compat/test/fixtures/package.json b/tests/node_compat/test/fixtures/package.json
deleted file mode 100644
index 0967ef424bce67..00000000000000
--- a/tests/node_compat/test/fixtures/package.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/tests/node_compat/test/internet/package.json b/tests/node_compat/test/internet/package.json
deleted file mode 100644
index 0967ef424bce67..00000000000000
--- a/tests/node_compat/test/internet/package.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/tests/node_compat/test/parallel/package.json b/tests/node_compat/test/parallel/package.json
deleted file mode 100644
index 0967ef424bce67..00000000000000
--- a/tests/node_compat/test/parallel/package.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/tests/node_compat/test/pseudo-tty/package.json b/tests/node_compat/test/pseudo-tty/package.json
deleted file mode 100644
index 0967ef424bce67..00000000000000
--- a/tests/node_compat/test/pseudo-tty/package.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/tests/node_compat/test/pummel/package.json b/tests/node_compat/test/pummel/package.json
deleted file mode 100644
index 0967ef424bce67..00000000000000
--- a/tests/node_compat/test/pummel/package.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/tests/node_compat/test/sequential/package.json b/tests/node_compat/test/sequential/package.json
deleted file mode 100644
index 0967ef424bce67..00000000000000
--- a/tests/node_compat/test/sequential/package.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/tests/registry/npm/@denotest/install-launch-cjs-temp-dir/1.0.0/install.js b/tests/registry/npm/@denotest/install-launch-cjs-temp-dir/1.0.0/install.js
new file mode 100644
index 00000000000000..61aadeb8391390
--- /dev/null
+++ b/tests/registry/npm/@denotest/install-launch-cjs-temp-dir/1.0.0/install.js
@@ -0,0 +1,12 @@
+const tempDir = Deno.makeTempDirSync();
+try {
+  // should work requiring these because this was launched via a node binary entrypoint
+  Deno.writeTextFileSync(`${tempDir}/index.js`, "module.exports = require('./other');");
+  Deno.writeTextFileSync(`${tempDir}/other.js`, "module.exports = (a, b) => a + b;");
+  const add = require(`${tempDir}/index.js`);
+  if (add(1, 2) !== 3) {
+    throw new Error("FAILED");
+  }
+} finally {
+  Deno.removeSync(tempDir, { recursive: true });
+}
diff --git a/tests/registry/npm/@denotest/install-launch-cjs-temp-dir/1.0.0/package.json b/tests/registry/npm/@denotest/install-launch-cjs-temp-dir/1.0.0/package.json
new file mode 100644
index 00000000000000..c3cf8dc4c334e6
--- /dev/null
+++ b/tests/registry/npm/@denotest/install-launch-cjs-temp-dir/1.0.0/package.json
@@ -0,0 +1,7 @@
+{
+  "name": "@denotest/install-launch-cjs-temp-dir",
+  "version": "1.0.0",
+  "scripts": {
+    "install": "node install.js"
+  }
+}
\ No newline at end of file
diff --git a/tests/registry/npm/@denotest/install-no-ext/1.0.0/install/check.js b/tests/registry/npm/@denotest/install-no-ext/1.0.0/install/check.js
new file mode 100644
index 00000000000000..7d55c2481d1142
--- /dev/null
+++ b/tests/registry/npm/@denotest/install-no-ext/1.0.0/install/check.js
@@ -0,0 +1 @@
+require("./output");
diff --git a/tests/registry/npm/@denotest/install-no-ext/1.0.0/install/index.js b/tests/registry/npm/@denotest/install-no-ext/1.0.0/install/index.js
new file mode 100644
index 00000000000000..7d55c2481d1142
--- /dev/null
+++ b/tests/registry/npm/@denotest/install-no-ext/1.0.0/install/index.js
@@ -0,0 +1 @@
+require("./output");
diff --git a/tests/registry/npm/@denotest/install-no-ext/1.0.0/install/output.js b/tests/registry/npm/@denotest/install-no-ext/1.0.0/install/output.js
new file mode 100644
index 00000000000000..69668cd6251b0d
--- /dev/null
+++ b/tests/registry/npm/@denotest/install-no-ext/1.0.0/install/output.js
@@ -0,0 +1 @@
+console.log("SUCCESS");
diff --git a/tests/registry/npm/@denotest/install-no-ext/1.0.0/package.json b/tests/registry/npm/@denotest/install-no-ext/1.0.0/package.json
new file mode 100644
index 00000000000000..b9abed1f6e1efd
--- /dev/null
+++ b/tests/registry/npm/@denotest/install-no-ext/1.0.0/package.json
@@ -0,0 +1,7 @@
+{
+  "name": "@denotest/install-no-ext",
+  "version": "1.0.0",
+  "scripts": {
+    "install": "node install/check && node install"
+  }
+}
\ No newline at end of file
diff --git a/tests/specs/compile/detect_cjs/deno.json b/tests/specs/compile/detect_cjs/deno.json
deleted file mode 100644
index 35f64c86f49a8f..00000000000000
--- a/tests/specs/compile/detect_cjs/deno.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "unstable": [
-    "detect-cjs"
-  ]
-}
diff --git a/tests/specs/compile/detect_cjs/__test__.jsonc b/tests/specs/compile/package_json_type/__test__.jsonc
similarity index 100%
rename from tests/specs/compile/detect_cjs/__test__.jsonc
rename to tests/specs/compile/package_json_type/__test__.jsonc
diff --git a/tests/specs/compile/detect_cjs/add.js b/tests/specs/compile/package_json_type/add.js
similarity index 100%
rename from tests/specs/compile/detect_cjs/add.js
rename to tests/specs/compile/package_json_type/add.js
diff --git a/tests/specs/compile/detect_cjs/compile.out b/tests/specs/compile/package_json_type/compile.out
similarity index 100%
rename from tests/specs/compile/detect_cjs/compile.out
rename to tests/specs/compile/package_json_type/compile.out
diff --git a/tests/specs/compile/detect_cjs/main.js b/tests/specs/compile/package_json_type/main.js
similarity index 100%
rename from tests/specs/compile/detect_cjs/main.js
rename to tests/specs/compile/package_json_type/main.js
diff --git a/tests/specs/compile/detect_cjs/output.out b/tests/specs/compile/package_json_type/output.out
similarity index 100%
rename from tests/specs/compile/detect_cjs/output.out
rename to tests/specs/compile/package_json_type/output.out
diff --git a/tests/specs/compile/detect_cjs/package.json b/tests/specs/compile/package_json_type/package.json
similarity index 100%
rename from tests/specs/compile/detect_cjs/package.json
rename to tests/specs/compile/package_json_type/package.json
diff --git a/tests/specs/compile/detect_cjs/subtract.ts b/tests/specs/compile/package_json_type/subtract.ts
similarity index 100%
rename from tests/specs/compile/detect_cjs/subtract.ts
rename to tests/specs/compile/package_json_type/subtract.ts
diff --git a/tests/specs/eval/pkg_json_type_cjs/__test__.jsonc b/tests/specs/eval/pkg_json_type_cjs/__test__.jsonc
new file mode 100644
index 00000000000000..cd3804d773a32c
--- /dev/null
+++ b/tests/specs/eval/pkg_json_type_cjs/__test__.jsonc
@@ -0,0 +1,4 @@
+{
+  "args": "eval console.log(1)",
+  "output": "1\n"
+}
diff --git a/tests/specs/eval/pkg_json_type_cjs/package.json b/tests/specs/eval/pkg_json_type_cjs/package.json
new file mode 100644
index 00000000000000..5bbefffbabee39
--- /dev/null
+++ b/tests/specs/eval/pkg_json_type_cjs/package.json
@@ -0,0 +1,3 @@
+{
+  "type": "commonjs"
+}
diff --git a/tests/specs/install/scripts_install_launch_cjs_temp_dir/__test__.jsonc b/tests/specs/install/scripts_install_launch_cjs_temp_dir/__test__.jsonc
new file mode 100644
index 00000000000000..087d08eff06892
--- /dev/null
+++ b/tests/specs/install/scripts_install_launch_cjs_temp_dir/__test__.jsonc
@@ -0,0 +1,5 @@
+{
+  "tempDir": true,
+  "args": "install --allow-scripts",
+  "output": "output.out"
+}
diff --git a/tests/specs/install/scripts_install_launch_cjs_temp_dir/output.out b/tests/specs/install/scripts_install_launch_cjs_temp_dir/output.out
new file mode 100644
index 00000000000000..d5f06cc6eac75a
--- /dev/null
+++ b/tests/specs/install/scripts_install_launch_cjs_temp_dir/output.out
@@ -0,0 +1,4 @@
+Download http://localhost:4260/@denotest%2finstall-launch-cjs-temp-dir
+Download http://localhost:4260/@denotest/install-launch-cjs-temp-dir/1.0.0.tgz
+Initialize @denotest/install-launch-cjs-temp-dir@1.0.0
+Initialize @denotest/install-launch-cjs-temp-dir@1.0.0: running 'install' script
diff --git a/tests/specs/install/scripts_install_launch_cjs_temp_dir/package.json b/tests/specs/install/scripts_install_launch_cjs_temp_dir/package.json
new file mode 100644
index 00000000000000..71672f9bc1b353
--- /dev/null
+++ b/tests/specs/install/scripts_install_launch_cjs_temp_dir/package.json
@@ -0,0 +1,5 @@
+{
+  "dependencies": {
+    "@denotest/install-launch-cjs-temp-dir": "*"
+  }
+}
diff --git a/tests/specs/install/scripts_install_no_ext/__test__.jsonc b/tests/specs/install/scripts_install_no_ext/__test__.jsonc
new file mode 100644
index 00000000000000..087d08eff06892
--- /dev/null
+++ b/tests/specs/install/scripts_install_no_ext/__test__.jsonc
@@ -0,0 +1,5 @@
+{
+  "tempDir": true,
+  "args": "install --allow-scripts",
+  "output": "output.out"
+}
diff --git a/tests/specs/install/scripts_install_no_ext/output.out b/tests/specs/install/scripts_install_no_ext/output.out
new file mode 100644
index 00000000000000..074e9781216645
--- /dev/null
+++ b/tests/specs/install/scripts_install_no_ext/output.out
@@ -0,0 +1,4 @@
+Download http://localhost:4260/@denotest%2finstall-no-ext
+Download http://localhost:4260/@denotest/install-no-ext/1.0.0.tgz
+Initialize @denotest/install-no-ext@1.0.0
+Initialize @denotest/install-no-ext@1.0.0: running 'install' script
diff --git a/tests/specs/install/scripts_install_no_ext/package.json b/tests/specs/install/scripts_install_no_ext/package.json
new file mode 100644
index 00000000000000..7ac9ca6b29fb6d
--- /dev/null
+++ b/tests/specs/install/scripts_install_no_ext/package.json
@@ -0,0 +1,5 @@
+{
+  "dependencies": {
+    "@denotest/install-no-ext": "*"
+  }
+}
diff --git a/tests/specs/mod.rs b/tests/specs/mod.rs
index 34221dd9da63d1..b4c8781d312caf 100644
--- a/tests/specs/mod.rs
+++ b/tests/specs/mod.rs
@@ -119,6 +119,9 @@ struct MultiStepMetaData {
   /// steps.
   #[serde(default)]
   pub temp_dir: bool,
+  /// Whether the temporary directory should be symlinked to another path.
+  #[serde(default)]
+  pub symlinked_temp_dir: bool,
   /// The base environment to use for the test.
   #[serde(default)]
   pub base: Option<String>,
@@ -142,6 +145,8 @@ struct SingleTestMetaData {
   #[serde(default)]
   pub temp_dir: bool,
   #[serde(default)]
+  pub symlinked_temp_dir: bool,
+  #[serde(default)]
   pub repeat: Option<usize>,
   #[serde(flatten)]
   pub step: StepMetaData,
@@ -155,6 +160,7 @@ impl SingleTestMetaData {
       base: self.base,
       cwd: None,
       temp_dir: self.temp_dir,
+      symlinked_temp_dir: self.symlinked_temp_dir,
       repeat: self.repeat,
       envs: Default::default(),
       steps: vec![self.step],
@@ -330,6 +336,20 @@ fn test_context_from_metadata(
     builder = builder.cwd(cwd.to_string_lossy());
   }
 
+  if metadata.symlinked_temp_dir {
+    // not actually deprecated, we just want to discourage its use
+    // because it's mostly used for testing purposes locally
+    #[allow(deprecated)]
+    {
+      builder = builder.use_symlinked_temp_dir();
+    }
+    if cfg!(not(debug_assertions)) {
+      // panic to prevent using this on the CI as CI already uses
+      // a symlinked temp directory for every test
+      panic!("Cannot use symlinkedTempDir in release mode");
+    }
+  }
+
   match &metadata.base {
     // todo(dsherret): add bases in the future as needed
     Some(base) => panic!("Unknown test base: {}", base),
diff --git a/tests/specs/npm/dual_cjs_esm/__test__.jsonc b/tests/specs/npm/dual_cjs_esm/__test__.jsonc
deleted file mode 100644
index f2b0d694e3b559..00000000000000
--- a/tests/specs/npm/dual_cjs_esm/__test__.jsonc
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "args": "run -A --quiet dual_cjs_esm/main.ts",
-  "output": "dual_cjs_esm/main.out"
-}
diff --git a/tests/specs/npm/dual_cjs_esm/cjs_referrer/__test__.jsonc b/tests/specs/npm/dual_cjs_esm/cjs_referrer/__test__.jsonc
new file mode 100644
index 00000000000000..de2c1a0bc5934a
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/cjs_referrer/__test__.jsonc
@@ -0,0 +1,14 @@
+{
+  "tempDir": true,
+  "tests": {
+    "check": {
+      "args": "check --node-modules-dir=auto main.cts",
+      "output": "check.out",
+      "exitCode": 1
+    },
+    "run": {
+      "args": "run --node-modules-dir=auto --allow-read main.cts",
+      "output": "main.out"
+    }
+  }
+}
diff --git a/tests/specs/npm/dual_cjs_esm/cjs_referrer/check.out b/tests/specs/npm/dual_cjs_esm/cjs_referrer/check.out
new file mode 100644
index 00000000000000..267d31fb7564a4
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/cjs_referrer/check.out
@@ -0,0 +1,8 @@
+Download http://localhost:4260/@denotest%2fdual-cjs-esm
+Download http://localhost:4260/@denotest/dual-cjs-esm/1.0.0.tgz
+Initialize @denotest/dual-cjs-esm@1.0.0
+Check file:///[WILDLINE]/main.cts
+error: TS2322 [ERROR]: Type '"cjs"' is not assignable to type '"other"'.
+const kind: "other" = mod.getKind();
+      ~~~~
+    at file:///[WILDLINE]/main.cts:3:7
diff --git a/tests/specs/npm/dual_cjs_esm/cjs_referrer/main.cts b/tests/specs/npm/dual_cjs_esm/cjs_referrer/main.cts
new file mode 100644
index 00000000000000..b8dd343f8ba4ac
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/cjs_referrer/main.cts
@@ -0,0 +1,4 @@
+import mod = require("@denotest/dual-cjs-esm");
+
+const kind: "other" = mod.getKind();
+console.log(kind);
diff --git a/tests/specs/npm/dual_cjs_esm/cjs_referrer/main.out b/tests/specs/npm/dual_cjs_esm/cjs_referrer/main.out
new file mode 100644
index 00000000000000..62ddbf47931db6
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/cjs_referrer/main.out
@@ -0,0 +1,4 @@
+Download http://localhost:4260/@denotest%2fdual-cjs-esm
+Download http://localhost:4260/@denotest/dual-cjs-esm/1.0.0.tgz
+Initialize @denotest/dual-cjs-esm@1.0.0
+cjs
diff --git a/tests/specs/npm/dual_cjs_esm/cjs_referrer/package.json b/tests/specs/npm/dual_cjs_esm/cjs_referrer/package.json
new file mode 100644
index 00000000000000..e1b1e1a5f8f1cf
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/cjs_referrer/package.json
@@ -0,0 +1,5 @@
+{
+  "dependencies": {
+    "@denotest/dual-cjs-esm": "*"
+  }
+}
diff --git a/tests/specs/npm/dual_cjs_esm/esm_referrer/__test__.jsonc b/tests/specs/npm/dual_cjs_esm/esm_referrer/__test__.jsonc
new file mode 100644
index 00000000000000..0ef147253652df
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/esm_referrer/__test__.jsonc
@@ -0,0 +1,4 @@
+{
+  "args": "run -A --quiet main.ts",
+  "output": "main.out"
+}
diff --git a/tests/specs/npm/dual_cjs_esm/dual_cjs_esm/main.out b/tests/specs/npm/dual_cjs_esm/esm_referrer/main.out
similarity index 100%
rename from tests/specs/npm/dual_cjs_esm/dual_cjs_esm/main.out
rename to tests/specs/npm/dual_cjs_esm/esm_referrer/main.out
diff --git a/tests/specs/npm/dual_cjs_esm/dual_cjs_esm/main.ts b/tests/specs/npm/dual_cjs_esm/esm_referrer/main.ts
similarity index 100%
rename from tests/specs/npm/dual_cjs_esm/dual_cjs_esm/main.ts
rename to tests/specs/npm/dual_cjs_esm/esm_referrer/main.ts
diff --git a/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/__test__.jsonc b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/__test__.jsonc
new file mode 100644
index 00000000000000..cf19217d18f200
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/__test__.jsonc
@@ -0,0 +1,14 @@
+{
+  "tempDir": true,
+  "tests": {
+    "check": {
+      "args": "check --node-modules-dir=auto main.ts",
+      "output": "check.out",
+      "exitCode": 1
+    },
+    "run": {
+      "args": "run --node-modules-dir=auto --allow-read main.ts",
+      "output": "main.out"
+    }
+  }
+}
diff --git a/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/check.out b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/check.out
new file mode 100644
index 00000000000000..cbd7740a9f6e90
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/check.out
@@ -0,0 +1,8 @@
+Download http://localhost:4260/@denotest%2fdual-cjs-esm
+Download http://localhost:4260/@denotest/dual-cjs-esm/1.0.0.tgz
+Initialize @denotest/dual-cjs-esm@1.0.0
+Check file:///[WILDLINE]/main.ts
+error: TS2322 [ERROR]: Type '"cjs"' is not assignable to type '"other"'.
+const kind: "other" = mod.getKind();
+      ~~~~
+    at file:///[WILDLINE]/main.ts:3:7
diff --git a/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/main.out b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/main.out
new file mode 100644
index 00000000000000..62ddbf47931db6
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/main.out
@@ -0,0 +1,4 @@
+Download http://localhost:4260/@denotest%2fdual-cjs-esm
+Download http://localhost:4260/@denotest/dual-cjs-esm/1.0.0.tgz
+Initialize @denotest/dual-cjs-esm@1.0.0
+cjs
diff --git a/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/main.ts b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/main.ts
new file mode 100644
index 00000000000000..b8dd343f8ba4ac
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/main.ts
@@ -0,0 +1,4 @@
+import mod = require("@denotest/dual-cjs-esm");
+
+const kind: "other" = mod.getKind();
+console.log(kind);
diff --git a/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/package.json b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/package.json
new file mode 100644
index 00000000000000..419d3d9f11ee25
--- /dev/null
+++ b/tests/specs/npm/dual_cjs_esm/ts_referrer_type_cjs/package.json
@@ -0,0 +1,6 @@
+{
+  "type": "commonjs",
+  "dependencies": {
+    "@denotest/dual-cjs-esm": "*"
+  }
+}
diff --git a/tests/specs/npm/permissions_outside_package/__test__.jsonc b/tests/specs/npm/permissions_outside_package/__test__.jsonc
index 56228296b3ed95..d5f6bf49081153 100644
--- a/tests/specs/npm/permissions_outside_package/__test__.jsonc
+++ b/tests/specs/npm/permissions_outside_package/__test__.jsonc
@@ -1,4 +1,5 @@
 {
-  "args": "run --allow-read permissions_outside_package/main.ts",
-  "output": "permissions_outside_package/main.out"
+  "tempDir": true,
+  "args": "run --allow-read --node-modules-dir=none main.ts",
+  "output": "main.out"
 }
diff --git a/tests/specs/npm/permissions_outside_package/permissions_outside_package/foo/config.js b/tests/specs/npm/permissions_outside_package/foo/config.js
similarity index 100%
rename from tests/specs/npm/permissions_outside_package/permissions_outside_package/foo/config.js
rename to tests/specs/npm/permissions_outside_package/foo/config.js
diff --git a/tests/specs/npm/permissions_outside_package/foo/package.json b/tests/specs/npm/permissions_outside_package/foo/package.json
new file mode 100644
index 00000000000000..95b43077e4ab30
--- /dev/null
+++ b/tests/specs/npm/permissions_outside_package/foo/package.json
@@ -0,0 +1,5 @@
+{
+  "name": "foobar",
+  "version": "0.0.1",
+  "type": "commonjs"
+}
diff --git a/tests/specs/npm/permissions_outside_package/permissions_outside_package/main.out b/tests/specs/npm/permissions_outside_package/main.out
similarity index 100%
rename from tests/specs/npm/permissions_outside_package/permissions_outside_package/main.out
rename to tests/specs/npm/permissions_outside_package/main.out
diff --git a/tests/specs/npm/permissions_outside_package/permissions_outside_package/main.ts b/tests/specs/npm/permissions_outside_package/main.ts
similarity index 64%
rename from tests/specs/npm/permissions_outside_package/permissions_outside_package/main.ts
rename to tests/specs/npm/permissions_outside_package/main.ts
index 934a3eebcfaf7c..a80713fa7233d7 100644
--- a/tests/specs/npm/permissions_outside_package/permissions_outside_package/main.ts
+++ b/tests/specs/npm/permissions_outside_package/main.ts
@@ -1,5 +1,5 @@
 import { loadConfigFile } from "npm:@denotest/permissions-outside-package";
 
-const fileName = `${Deno.cwd()}/permissions_outside_package/foo/config.js`;
+const fileName = `${Deno.cwd()}/foo/config.js`;
 const config = loadConfigFile(fileName);
 console.log(config);
diff --git a/tests/specs/npm/permissions_outside_package/package.json b/tests/specs/npm/permissions_outside_package/package.json
new file mode 100644
index 00000000000000..2c63c0851048d8
--- /dev/null
+++ b/tests/specs/npm/permissions_outside_package/package.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/specs/npm/permissions_outside_package/permissions_outside_package/foo/package.json b/tests/specs/npm/permissions_outside_package/permissions_outside_package/foo/package.json
deleted file mode 100644
index cc049e6ce909c0..00000000000000
--- a/tests/specs/npm/permissions_outside_package/permissions_outside_package/foo/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "foobar",
-  "version": "0.0.1"
-}
diff --git a/tests/specs/run/import_common_js/__test__.jsonc b/tests/specs/run/import_common_js/__test__.jsonc
index 6510dbad7bcd80..0602a09baa6d69 100644
--- a/tests/specs/run/import_common_js/__test__.jsonc
+++ b/tests/specs/run/import_common_js/__test__.jsonc
@@ -1,21 +1,27 @@
 {
-  "steps": [
-    { "args": "run -R index.cjs", "output": "index.out" },
-    { "args": "run -R main.ts", "output": "main.out" },
-    {
+  "tests": {
+    "cjs_entrypoint": {
+      "args": "run -R index.cjs",
+      "output": "index.out"
+    },
+    "esm_entrypoint": {
+      "args": "run -R main.ts",
+      "output": "main.out"
+    },
+    "module_error": {
       "args": "run module_error.js",
       "output": "module_error.out",
       "exitCode": 1
     },
-    {
+    "exports_error": {
       "args": "run exports_error.js",
       "output": "exports_error.out",
       "exitCode": 1
     },
-    {
+    "require_error": {
       "args": "run require_error.js",
       "output": "require_error.out",
       "exitCode": 1
     }
-  ]
+  }
 }
diff --git a/tests/specs/run/import_common_js/a.js b/tests/specs/run/import_common_js/a.js
deleted file mode 100644
index c465ab588b3a6d..00000000000000
--- a/tests/specs/run/import_common_js/a.js
+++ /dev/null
@@ -1,7 +0,0 @@
-function foobar() {
-  console.log("foobar");
-}
-
-module.exports = {
-  foobar,
-};
diff --git a/tests/specs/run/import_common_js/exports_error.out b/tests/specs/run/import_common_js/exports_error.out
index b979cce5c78e65..baa44682be52f6 100644
--- a/tests/specs/run/import_common_js/exports_error.out
+++ b/tests/specs/run/import_common_js/exports_error.out
@@ -3,10 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
                       ^
     at [WILDCARD]exports_error.js:1:23
 
-    info: Deno supports CommonJS modules in .cjs files, or when there's a package.json
-          with "type": "commonjs" option and --unstable-detect-cjs flag is used.
+    info: Deno supports CommonJS modules in .cjs files, or when the closest
+          package.json has a "type": "commonjs" option.
     hint: Rewrite this module to ESM,
           or change the file extension to .cjs,
-          or add package.json next to the file with "type": "commonjs" option
-          and pass --unstable-detect-cjs flag.
+          or add package.json next to the file with "type": "commonjs" option.
     docs: https://docs.deno.com/go/commonjs
diff --git a/tests/specs/run/import_common_js/index.cjs b/tests/specs/run/import_common_js/index.cjs
index 18caf81e941ec7..0026e237d1c21a 100644
--- a/tests/specs/run/import_common_js/index.cjs
+++ b/tests/specs/run/import_common_js/index.cjs
@@ -1,9 +1,7 @@
 const process = require("process");
-const a = require("./a");
 
 console.log(process.cwd());
 
 module.exports = {
   cwd: process.cwd,
-  foobar: a.foobar,
 };
diff --git a/tests/specs/run/import_common_js/index.out b/tests/specs/run/import_common_js/index.out
index 3650631b7a2320..6a734b9948c861 100644
--- a/tests/specs/run/import_common_js/index.out
+++ b/tests/specs/run/import_common_js/index.out
@@ -1 +1 @@
-[WILDCARD]import_common_js
+[WILDLINE]import_common_js
diff --git a/tests/specs/run/import_common_js/main.out b/tests/specs/run/import_common_js/main.out
index 03301b36209602..9df31297548e70 100644
--- a/tests/specs/run/import_common_js/main.out
+++ b/tests/specs/run/import_common_js/main.out
@@ -1,5 +1,3 @@
 hello from foo node module
 [WILDCARD]import_common_js
 cjsModule.cwd() [WILDCARD]import_common_js
-foobar
-cjsModule.foobar() undefined
diff --git a/tests/specs/run/import_common_js/module_error.out b/tests/specs/run/import_common_js/module_error.out
index 654ee838dd13b4..957b19cb1e6857 100644
--- a/tests/specs/run/import_common_js/module_error.out
+++ b/tests/specs/run/import_common_js/module_error.out
@@ -3,10 +3,9 @@ module.exports = {
 ^
     at [WILDCARD]module_error.js:1:1
 
-    info: Deno supports CommonJS modules in .cjs files, or when there's a package.json
-          with "type": "commonjs" option and --unstable-detect-cjs flag is used.
+    info: Deno supports CommonJS modules in .cjs files, or when the closest
+          package.json has a "type": "commonjs" option.
     hint: Rewrite this module to ESM,
           or change the file extension to .cjs,
-          or add package.json next to the file with "type": "commonjs" option
-          and pass --unstable-detect-cjs flag.
+          or add package.json next to the file with "type": "commonjs" option.
     docs: https://docs.deno.com/go/commonjs
diff --git a/tests/specs/run/import_common_js/node_modules/foo/index.mjs b/tests/specs/run/import_common_js/node_modules/foo/index.mjs
index cc93554c73c3fd..7a11d39ae60266 100644
--- a/tests/specs/run/import_common_js/node_modules/foo/index.mjs
+++ b/tests/specs/run/import_common_js/node_modules/foo/index.mjs
@@ -10,5 +10,4 @@ export default async function () {
     const cjsModule = await import(url.pathToFileURL(cjsFileToImport));
 
     console.log("cjsModule.cwd()", cjsModule.cwd());
-    console.log("cjsModule.foobar()", cjsModule.foobar());
 }
diff --git a/tests/specs/run/import_common_js/require_error.out b/tests/specs/run/import_common_js/require_error.out
index 81ffd6591f570e..e13db85e8e85df 100644
--- a/tests/specs/run/import_common_js/require_error.out
+++ b/tests/specs/run/import_common_js/require_error.out
@@ -3,10 +3,9 @@ const process = require("process");
                 ^
     at [WILDCARD]require_error.js:1:17
 
-    info: Deno supports CommonJS modules in .cjs files, or when there's a package.json
-          with "type": "commonjs" option and --unstable-detect-cjs flag is used.
+    info: Deno supports CommonJS modules in .cjs files, or when the closest
+          package.json has a "type": "commonjs" option.
     hint: Rewrite this module to ESM,
           or change the file extension to .cjs,
-          or add package.json next to the file with "type": "commonjs" option
-          and pass --unstable-detect-cjs flag.
+          or add package.json next to the file with "type": "commonjs" option.
     docs: https://docs.deno.com/go/commonjs
diff --git a/tests/specs/run/npm_pkg_requires_esm_js/__test__.jsonc b/tests/specs/run/npm_pkg_requires_esm_js/__test__.jsonc
new file mode 100644
index 00000000000000..3da8db40485e31
--- /dev/null
+++ b/tests/specs/run/npm_pkg_requires_esm_js/__test__.jsonc
@@ -0,0 +1,5 @@
+{
+  "args": "run -A main.js",
+  "output": "output.out",
+  "exitCode": 1
+}
diff --git a/tests/specs/run/npm_pkg_requires_esm_js/file.js b/tests/specs/run/npm_pkg_requires_esm_js/file.js
new file mode 100644
index 00000000000000..d9536a69b3f87d
--- /dev/null
+++ b/tests/specs/run/npm_pkg_requires_esm_js/file.js
@@ -0,0 +1 @@
+console.log(import.meta.url);
diff --git a/tests/specs/run/npm_pkg_requires_esm_js/logs_require.js b/tests/specs/run/npm_pkg_requires_esm_js/logs_require.js
new file mode 100644
index 00000000000000..984e1f3e74541b
--- /dev/null
+++ b/tests/specs/run/npm_pkg_requires_esm_js/logs_require.js
@@ -0,0 +1 @@
+console.log(require);
diff --git a/tests/specs/run/npm_pkg_requires_esm_js/main.js b/tests/specs/run/npm_pkg_requires_esm_js/main.js
new file mode 100644
index 00000000000000..3704c8bf68031f
--- /dev/null
+++ b/tests/specs/run/npm_pkg_requires_esm_js/main.js
@@ -0,0 +1,5 @@
+import doRequire from "package";
+import path from "node:path";
+
+doRequire(path.resolve(import.meta.dirname, "file.js"));
+doRequire(path.resolve(import.meta.dirname, "logs_require.js"));
diff --git a/tests/specs/run/npm_pkg_requires_esm_js/node_modules/package/index.js b/tests/specs/run/npm_pkg_requires_esm_js/node_modules/package/index.js
new file mode 100644
index 00000000000000..5d787237139888
--- /dev/null
+++ b/tests/specs/run/npm_pkg_requires_esm_js/node_modules/package/index.js
@@ -0,0 +1,3 @@
+module.exports = (file) => {
+  return require(file);
+};
diff --git a/tests/specs/run/npm_pkg_requires_esm_js/node_modules/package/package.json b/tests/specs/run/npm_pkg_requires_esm_js/node_modules/package/package.json
new file mode 100644
index 00000000000000..5723987e9f4279
--- /dev/null
+++ b/tests/specs/run/npm_pkg_requires_esm_js/node_modules/package/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "package",
+  "version": "1.0.0"
+}
\ No newline at end of file
diff --git a/tests/specs/run/npm_pkg_requires_esm_js/output.out b/tests/specs/run/npm_pkg_requires_esm_js/output.out
new file mode 100644
index 00000000000000..2cae7108b073d8
--- /dev/null
+++ b/tests/specs/run/npm_pkg_requires_esm_js/output.out
@@ -0,0 +1,12 @@
+file:///[WILDLINE]/file.js
+error: Uncaught (in promise) ReferenceError: require is not defined
+console.log(require);
+            ^
+    at [WILDCARD]
+
+    info: Deno supports CommonJS modules in .cjs files, or when the closest
+          package.json has a "type": "commonjs" option.
+    hint: Rewrite this module to ESM,
+          or change the file extension to .cjs,
+          or add package.json next to the file with "type": "commonjs" option.
+    docs: https://docs.deno.com/go/commonjs
diff --git a/tests/specs/run/npm_pkg_requires_esm_js/package.json b/tests/specs/run/npm_pkg_requires_esm_js/package.json
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/tests/specs/run/package_json_type/commonjs/basic/deno.jsonc b/tests/specs/run/package_json_type/commonjs/basic/deno.jsonc
deleted file mode 100644
index 35f64c86f49a8f..00000000000000
--- a/tests/specs/run/package_json_type/commonjs/basic/deno.jsonc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "unstable": [
-    "detect-cjs"
-  ]
-}
diff --git a/tests/specs/run/package_json_type/commonjs/basic/main_mix.out b/tests/specs/run/package_json_type/commonjs/basic/main_mix.out
index 78f421644cd519..65671fd618a9a2 100644
--- a/tests/specs/run/package_json_type/commonjs/basic/main_mix.out
+++ b/tests/specs/run/package_json_type/commonjs/basic/main_mix.out
@@ -4,10 +4,9 @@ console.log(require("./add").add(1, 2));
         ^
     at file:///[WILDLINE]main_mix.js:[WILDLINE]
 
-    info: Deno supports CommonJS modules in .cjs files, or when there's a package.json
-          with "type": "commonjs" option and --unstable-detect-cjs flag is used.
+    info: Deno supports CommonJS modules in .cjs files, or when the closest
+          package.json has a "type": "commonjs" option.
     hint: Rewrite this module to ESM,
           or change the file extension to .cjs,
-          or add package.json next to the file with "type": "commonjs" option
-          and pass --unstable-detect-cjs flag.
+          or add package.json next to the file with "type": "commonjs" option.
     docs: https://docs.deno.com/go/commonjs
diff --git a/tests/specs/run/package_json_type/commonjs/jsx/deno.jsonc b/tests/specs/run/package_json_type/commonjs/jsx/deno.jsonc
index 192ddb98c4130f..31d05ffb7482e1 100644
--- a/tests/specs/run/package_json_type/commonjs/jsx/deno.jsonc
+++ b/tests/specs/run/package_json_type/commonjs/jsx/deno.jsonc
@@ -3,8 +3,5 @@
   "compilerOptions": {
     "jsx": "react-jsx",
     "jsxImportSource": "react"
-  },
-  "unstable": [
-    "detect-cjs"
-  ]
+  }
 }
diff --git a/tests/specs/run/package_json_type/none/deno.jsonc b/tests/specs/run/package_json_type/none/deno.jsonc
deleted file mode 100644
index 35f64c86f49a8f..00000000000000
--- a/tests/specs/run/package_json_type/none/deno.jsonc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "unstable": [
-    "detect-cjs"
-  ]
-}
diff --git a/tests/specs/run/package_json_type/none/main_cjs.out b/tests/specs/run/package_json_type/none/main_cjs.out
index 8d34808fb113f9..afa5028f4f814f 100644
--- a/tests/specs/run/package_json_type/none/main_cjs.out
+++ b/tests/specs/run/package_json_type/none/main_cjs.out
@@ -3,10 +3,9 @@ const { add } = require("./add");
                 ^
     at file:///[WILDLINE]
 
-    info: Deno supports CommonJS modules in .cjs files, or when there's a package.json
-          with "type": "commonjs" option and --unstable-detect-cjs flag is used.
+    info: Deno supports CommonJS modules in .cjs files, or when the closest
+          package.json has a "type": "commonjs" option.
     hint: Rewrite this module to ESM,
           or change the file extension to .cjs,
-          or add package.json next to the file with "type": "commonjs" option
-          and pass --unstable-detect-cjs flag.
+          or add package.json next to the file with "type": "commonjs" option.
     docs: https://docs.deno.com/go/commonjs
diff --git a/tests/specs/run/require_esm/main.out b/tests/specs/run/require_esm/main.out
index 57b842b345f791..4890e1a492de05 100644
--- a/tests/specs/run/require_esm/main.out
+++ b/tests/specs/run/require_esm/main.out
@@ -1,13 +1,4 @@
 [Module: null prototype] { sync_js: 1 }
 [Module: null prototype] { sync_mjs: 1 }
 error: Uncaught (in promise) Error: Top-level await is not allowed in synchronous evaluation
-    at loadESMFromCJS (node:module:[WILDCARD])
-    at Module._compile (node:module:[WILDCARD])
-    at Object.Module._extensions..js (node:module:[WILDCARD])
-    at Module.load (node:module:[WILDCARD])
-    at Function.Module._load (node:module:[WILDCARD])
-    at Module.require (node:module:[WILDCARD])
-    at require (node:module:[WILDCARD])
-    at Object.<anonymous> (file:[WILDCARD]/tests/specs/run/require_esm/main.cjs:[WILDCARD])
-    at Object.<anonymous> (file:[WILDCARD]/tests/specs/run/require_esm/main.cjs:[WILDCARD])
-    at Module._compile (node:module:[WILDCARD])
+    at [WILDCARD]
diff --git a/tests/specs/run/stdin_type_cjs/__test__.jsonc b/tests/specs/run/stdin_type_cjs/__test__.jsonc
new file mode 100644
index 00000000000000..e60af4a80335c5
--- /dev/null
+++ b/tests/specs/run/stdin_type_cjs/__test__.jsonc
@@ -0,0 +1,5 @@
+{
+  "args": "run --quiet -",
+  "output": "1\n",
+  "input": "console.log(1)"
+}
diff --git a/tests/specs/run/stdin_type_cjs/package.json b/tests/specs/run/stdin_type_cjs/package.json
new file mode 100644
index 00000000000000..5bbefffbabee39
--- /dev/null
+++ b/tests/specs/run/stdin_type_cjs/package.json
@@ -0,0 +1,3 @@
+{
+  "type": "commonjs"
+}
diff --git a/tests/specs/run/stdin_type_cjs/stdin_read_all.ts b/tests/specs/run/stdin_type_cjs/stdin_read_all.ts
new file mode 100644
index 00000000000000..2ecae40b716c3e
--- /dev/null
+++ b/tests/specs/run/stdin_type_cjs/stdin_read_all.ts
@@ -0,0 +1 @@
+Deno.stdin.readable.pipeTo(Deno.stdout.writable);
diff --git a/tests/specs/schema.json b/tests/specs/schema.json
index 8f3953ee44477e..2b35d9bd7dba4b 100644
--- a/tests/specs/schema.json
+++ b/tests/specs/schema.json
@@ -36,6 +36,9 @@
         "flaky": {
           "type": "boolean"
         },
+        "symlinkedTempDir": {
+          "type": "boolean"
+        },
         "if": {
           "type": "string",
           "examples": [
diff --git a/tests/util/server/src/servers/mod.rs b/tests/util/server/src/servers/mod.rs
index d9adde542033fe..0b1d99aeb92751 100644
--- a/tests/util/server/src/servers/mod.rs
+++ b/tests/util/server/src/servers/mod.rs
@@ -807,17 +807,17 @@ async fn main_server(
     (_, "/jsx/jsx-runtime") | (_, "/jsx/jsx-dev-runtime") => {
       let mut res = Response::new(string_body(
         r#"export function jsx(
-          _type,
-          _props,
-          _key,
-          _source,
-          _self,
-        ) {}
-        export const jsxs = jsx;
-        export const jsxDEV = jsx;
-        export const Fragment = Symbol("Fragment");
-        console.log("imported", import.meta.url);
-        "#,
+  _type,
+  _props,
+  _key,
+  _source,
+  _self,
+) {}
+export const jsxs = jsx;
+export const jsxDEV = jsx;
+export const Fragment = Symbol("Fragment");
+console.log("imported", import.meta.url);
+"#,
       ));
       res.headers_mut().insert(
         "Content-type",