// Copyright 2018-2026 the Deno authors. MIT license.

use std::borrow::Cow;
use std::cell::RefCell;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;

use boxed_error::Boxed;
use deno_core::FastString;
use deno_core::JsRuntimeInspector;
use deno_core::OpState;
use deno_core::op2;
use deno_core::url::Url;
use deno_core::v8;
use deno_error::JsErrorBox;
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 deno_permissions::PermissionsContainer;
use node_resolver::InNpmPackageChecker;
use node_resolver::NodeResolutionKind;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::ResolutionMode;
use node_resolver::UrlOrPath;
use node_resolver::UrlOrPathRef;
use node_resolver::cache::NodeResolutionThreadLocalCache;
use node_resolver::errors::PackageJsonLoadError;
use sys_traits::FsMetadataValue;

use crate::ExtNodeSys;
use crate::NodeRequireLoaderRc;
use crate::NodeResolverRc;
use crate::PackageJsonResolverRc;

#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a>(
  state: &mut OpState,
  file_path: Cow<'a, Path>,
) -> Result<Cow<'a, Path>, JsErrorBox> {
  // Fast path: when read is fully granted there's nothing to check, so skip
  // fetching the loader and the per-call work it does (e.g. module graph
  // lookups) entirely.
  if state.borrow::<PermissionsContainer>().query_read_all() {
    return Ok(file_path);
  }
  let loader = state.borrow::<NodeRequireLoaderRc>().clone();
  let permissions = state.borrow_mut::<PermissionsContainer>();
  loader.ensure_read_permission(permissions, file_path)
}

#[derive(Debug, Boxed, deno_error::JsError)]
pub struct RequireError(pub Box<RequireErrorKind>);

#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum RequireErrorKind {
  #[class(inherit)]
  #[error(transparent)]
  UrlParse(
    #[from]
    #[inherit]
    url::ParseError,
  ),
  #[class(inherit)]
  #[error(transparent)]
  Permission(#[inherit] JsErrorBox),
  #[class(generic)]
  #[properties(inherit)]
  #[error(transparent)]
  PackageExportsResolve(
    #[from] node_resolver::errors::PackageExportsResolveError,
  ),
  #[class(generic)]
  #[properties(inherit)]
  #[error(transparent)]
  PackageJsonLoad(#[from] node_resolver::errors::PackageJsonLoadError),
  #[class(generic)]
  #[properties(inherit)]
  #[error(transparent)]
  PackageImportsResolve(
    #[from] node_resolver::errors::PackageImportsResolveError,
  ),
  #[class(generic)]
  #[properties(inherit)]
  #[error(transparent)]
  FilePathConversion(#[from] deno_path_util::UrlToFilePathError),
  #[class(generic)]
  #[properties(inherit)]
  #[error(transparent)]
  UrlConversion(#[from] deno_path_util::PathToUrlError),
  #[class(inherit)]
  #[error(transparent)]
  Fs(
    #[from]
    #[inherit]
    deno_io::fs::FsError,
  ),
  #[class(inherit)]
  #[error(transparent)]
  Io(
    #[from]
    #[inherit]
    std::io::Error,
  ),
  #[class(inherit)]
  #[error(transparent)]
  ReadModule(
    #[from]
    #[inherit]
    JsErrorBox,
  ),
  #[class(inherit)]
  #[error(transparent)]
  UnableToGetCwd(UnableToGetCwdError),
}

#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[error("Unable to get CWD")]
#[class(inherit)]
pub struct UnableToGetCwdError(#[source] pub std::io::Error);

#[op2]
pub fn op_require_init_paths() -> Vec<String> {
  // todo(dsherret): this code is node compat mode specific and
  // we probably don't want it for small mammal, so ignore it for now

  // let (home_dir, node_path) = if cfg!(windows) {
  //   (
  //     std::env::var("USERPROFILE").unwrap_or_else(|_| "".into()),
  //     std::env::var("NODE_PATH").unwrap_or_else(|_| "".into()),
  //   )
  // } else {
  //   (
  //     std::env::var("HOME").unwrap_or_else(|_| "".into()),
  //     std::env::var("NODE_PATH").unwrap_or_else(|_| "".into()),
  //   )
  // };

  // let mut prefix_dir = std::env::current_exe().unwrap();
  // if cfg!(windows) {
  //   prefix_dir = prefix_dir.join("..").join("..")
  // } else {
  //   prefix_dir = prefix_dir.join("..")
  // }

  // let mut paths = vec![prefix_dir.join("lib").join("node")];

  // if !home_dir.is_empty() {
  //   paths.insert(0, PathBuf::from(&home_dir).join(".node_libraries"));
  //   paths.insert(0, PathBuf::from(&home_dir).join(".nod_modules"));
  // }

  // let mut paths = paths
  //   .into_iter()
  //   .map(|p| p.to_string_lossy().into_owned())
  //   .collect();

  // if !node_path.is_empty() {
  //   let delimiter = if cfg!(windows) { ";" } else { ":" };
  //   let mut node_paths: Vec<String> = node_path
  //     .split(delimiter)
  //     .filter(|e| !e.is_empty())
  //     .map(|s| s.to_string())
  //     .collect();
  //   node_paths.append(&mut paths);
  //   paths = node_paths;
  // }

  vec![]
}

#[op2(stack_trace)]
pub fn op_require_node_module_paths<TSys: ExtNodeSys + 'static>(
  state: &mut OpState,
  #[string] from: &str,
) -> Result<Vec<String>, RequireError> {
  let sys = state.borrow::<TSys>();
  // Guarantee that "from" is absolute. Avoid calling `env_current_dir()`
  // when we don't need it — on macOS it walks the directory tree from `/`
  // and fails with EACCES if any ancestor is unreadable (see #21585), so
  // an unrelated absolute `from` would otherwise crash here.
  let from_path = Path::new(from);
  let from = if from.starts_with("file:///") {
    Cow::Owned(url_to_file_path(&Url::parse(from)?)?)
  } else if from_path.is_absolute() {
    normalize_path(Cow::Borrowed(from_path))
  } else {
    let current_dir = &sys
      .env_current_dir()
      .map_err(|e| RequireErrorKind::UnableToGetCwd(UnableToGetCwdError(e)))?;
    normalize_path(Cow::Owned(current_dir.join(from)))
  };

  if cfg!(windows) {
    // return root node_modules when path is 'D:\\'.
    let from_str = from.to_str().unwrap();
    if from_str.len() >= 3 {
      let bytes = from_str.as_bytes();
      if bytes[from_str.len() - 1] == b'\\' && bytes[from_str.len() - 2] == b':'
      {
        let p = format!("{}node_modules", from_str);
        return Ok(vec![p]);
      }
    }
  } else {
    // Return early not only to avoid unnecessary work, but to *avoid* returning
    // an array of two items for a root: [ '//node_modules', '/node_modules' ]
    if from.to_string_lossy() == "/" {
      return Ok(vec!["/node_modules".to_string()]);
    }
  }

  let loader = state.borrow::<NodeRequireLoaderRc>();
  Ok(loader.resolve_require_node_module_paths(&from))
}

#[op2]
#[string]
pub fn op_require_proxy_path(#[string] filename: &str) -> Option<String> {
  // Allow a directory to be passed as the filename
  let trailing_slash = if cfg!(windows) {
    // Node also counts a trailing forward slash as a
    // directory for node on Windows, but not backslashes
    // on non-Windows platforms
    filename.ends_with('\\') || filename.ends_with('/')
  } else {
    filename.ends_with('/')
  };

  if trailing_slash {
    let p = Path::new(filename);
    Some(p.join("noop.js").to_string_lossy().into_owned())
  } else {
    None // filename as-is
  }
}

#[op2(fast)]
pub fn op_require_is_request_relative(#[string] request: &str) -> bool {
  if request.starts_with("./")
    || request.starts_with("../")
    || request == "."
    || request == ".."
  {
    return true;
  }

  if cfg!(windows) {
    if request.starts_with(".\\") {
      return true;
    }

    if request.starts_with("..\\") {
      return true;
    }
  }

  false
}

#[op2]
#[string]
pub fn op_require_resolve_deno_dir<
  TInNpmPackageChecker: InNpmPackageChecker + 'static,
  TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
  TSys: ExtNodeSys + 'static,
>(
  state: &mut OpState,
  #[string] request: &str,
  #[string] parent_filename: &str,
) -> Result<Option<String>, deno_path_util::PathToUrlError> {
  let resolver = state.borrow::<NodeResolverRc<
    TInNpmPackageChecker,
    TNpmPackageFolderResolver,
    TSys,
  >>();

  let path = Path::new(parent_filename);
  if let Ok(folder) = resolver.resolve_package_folder_from_package(
    request,
    &UrlOrPathRef::from_path(path),
  ) {
    return Ok(Some(folder.to_string_lossy().into_owned()));
  }

  // Referrer-based resolution failed. When the referrer lives outside the
  // global cache (e.g. a user's project file invoking `require()` through
  // a hook installed by a package that *is* in the cache, mirroring the
  // Playwright config-transpile flow), the npm resolver has no way to
  // anchor the lookup. Fall back to resolving the bare specifier as a
  // top-level dependency in the npm graph.
  let referrer_is_in_npm_package = url_from_file_path(path)
    .map(|url| resolver.in_npm_package(&url))
    .unwrap_or(false);
  if referrer_is_in_npm_package {
    return Ok(None);
  }
  let package_name = bare_specifier_package_name(request);
  if package_name.is_empty() {
    return Ok(None);
  }
  let loader = state.borrow::<NodeRequireLoaderRc>();
  Ok(
    loader
      .resolve_package_folder_from_name(package_name)
      .map(|p| p.to_string_lossy().into_owned()),
  )
}

/// Returns the npm package name portion of a bare specifier such as
/// `pkg`, `pkg/sub`, `@scope/pkg`, or `@scope/pkg/sub`. Returns an empty
/// string for relative or otherwise invalid specifiers.
fn bare_specifier_package_name(specifier: &str) -> &str {
  if specifier.is_empty()
    || specifier.starts_with('.')
    || specifier.starts_with('/')
    || specifier.starts_with('#')
  {
    return "";
  }
  if let Some(rest) = specifier.strip_prefix('@') {
    let Some(scope_end) = rest.find('/') else {
      return "";
    };
    let after_scope = &rest[scope_end + 1..];
    match after_scope.find('/') {
      Some(rel) => &specifier[..1 + scope_end + 1 + rel],
      None => specifier,
    }
  } else {
    match specifier.find('/') {
      Some(i) => &specifier[..i],
      None => specifier,
    }
  }
}

#[op2(fast)]
pub fn op_require_is_deno_dir_package<
  TInNpmPackageChecker: InNpmPackageChecker + 'static,
  TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
  TSys: ExtNodeSys + 'static,
>(
  state: &mut OpState,
  #[string] path: &str,
) -> bool {
  let resolver = state.borrow::<NodeResolverRc<
    TInNpmPackageChecker,
    TNpmPackageFolderResolver,
    TSys,
  >>();
  match deno_path_util::url_from_file_path(Path::new(path)) {
    Ok(specifier) => resolver.in_npm_package(&specifier),
    Err(_) => false,
  }
}

#[op2]
pub fn op_require_resolve_lookup_paths(
  #[string] request: &str,
  #[scoped] maybe_parent_paths: Option<Vec<String>>,
  #[string] parent_filename: &str,
) -> Option<Vec<String>> {
  if !request.starts_with('.')
    || (request.len() > 1
      && !request.starts_with("..")
      && !request.starts_with("./")
      && (!cfg!(windows) || !request.starts_with(".\\")))
  {
    let module_paths = vec![];
    let mut paths = module_paths;
    if let Some(mut parent_paths) = maybe_parent_paths
      && !parent_paths.is_empty()
    {
      paths.append(&mut parent_paths);
    }

    if !paths.is_empty() {
      return Some(paths);
    } else {
      return None;
    }
  }

  // In REPL, parent.filename is null/empty.
  if parent_filename.is_empty() {
    // If parent has paths (e.g. fakeParent from require.resolve with
    // options.paths), use those. Otherwise fall back to cwd.
    if let Some(parent_paths) = maybe_parent_paths
      && !parent_paths.is_empty()
    {
      return Some(parent_paths);
    }
    return Some(vec![".".to_string()]);
  }

  let p = Path::new(parent_filename);
  Some(vec![p.parent().unwrap().to_string_lossy().into_owned()])
}

#[op2(fast)]
pub fn op_require_path_is_absolute(#[string] p: &str) -> bool {
  Path::new(p).is_absolute()
}

#[op2(fast, stack_trace)]
pub fn op_require_stat<TSys: ExtNodeSys + 'static>(
  state: &mut OpState,
  #[string] path: &str,
) -> Result<i32, JsErrorBox> {
  let path = Cow::Borrowed(Path::new(path));
  let path = if path.ends_with("node_modules") {
    // skip stat permission checks for node_modules directories
    // because they're noisy and it's fine
    path
  } else {
    ensure_read_permission(state, path)?
  };
  let sys = state.borrow::<TSys>();
  if let Ok(metadata) = sys.fs_metadata(&path) {
    if metadata.file_type().is_file() {
      return Ok(0);
    } else {
      return Ok(1);
    }
  }

  Ok(-1)
}

#[op2(stack_trace)]
#[string]
pub fn op_require_real_path<TSys: ExtNodeSys + 'static>(
  state: &mut OpState,
  #[string] request: &str,
) -> Result<String, RequireError> {
  let path = Cow::Borrowed(Path::new(request));
  let path = ensure_read_permission(state, path)
    .map_err(RequireErrorKind::Permission)?;
  let sys = state.borrow::<TSys>();
  let canonicalized_path =
    deno_path_util::strip_unc_prefix(match sys.fs_canonicalize(&path) {
      Ok(path) => path,
      Err(err) => {
        if path.ends_with("$deno$eval.cjs")
          || path.ends_with("$deno$eval.cts")
          || path.ends_with("$deno$stdin.cjs")
          || path.ends_with("$deno$stdin.cts")
        {
          path.to_path_buf()
        } else {
          return Err(RequireErrorKind::Io(err).into_box());
        }
      }
    });
  Ok(canonicalized_path.to_string_lossy().into_owned())
}

fn path_resolve<'a>(mut parts: impl Iterator<Item = &'a str>) -> PathBuf {
  let mut p = PathBuf::from(parts.next().unwrap());
  for part in parts {
    p = p.join(part);
  }
  normalize_path(Cow::Owned(p)).into_owned()
}

#[op2]
#[string]
pub fn op_require_path_resolve(#[scoped] parts: Vec<String>) -> String {
  path_resolve(parts.iter().map(|s| s.as_str()))
    .to_string_lossy()
    .into_owned()
}

#[op2]
#[string]
pub fn op_require_path_dirname(
  #[string] request: &str,
) -> Result<String, JsErrorBox> {
  let p = Path::new(request);
  if let Some(parent) = p.parent() {
    Ok(parent.to_string_lossy().into_owned())
  } else {
    Err(JsErrorBox::generic("Path doesn't have a parent"))
  }
}

#[op2]
#[string]
pub fn op_require_path_basename(
  #[string] request: &str,
) -> Result<String, JsErrorBox> {
  let p = Path::new(request);
  if let Some(path) = p.file_name() {
    Ok(path.to_string_lossy().into_owned())
  } else {
    Err(JsErrorBox::generic("Path doesn't have a file name"))
  }
}

#[op2(stack_trace)]
#[string]
pub fn op_require_try_self<
  TInNpmPackageChecker: InNpmPackageChecker + 'static,
  TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
  TSys: ExtNodeSys + 'static,
>(
  state: &mut OpState,
  #[string] parent_path: &str,
  #[string] request: &str,
) -> Result<Option<String>, RequireError> {
  let pkg_json_resolver = state.borrow::<PackageJsonResolverRc<TSys>>();
  let pkg = pkg_json_resolver
    .get_closest_package_json(Path::new(parent_path))
    .ok()
    .flatten();
  let Some(pkg) = pkg else {
    return Ok(None);
  };

  if pkg.exports.is_none() {
    return Ok(None);
  }
  let Some(pkg_name) = &pkg.name else {
    return Ok(None);
  };

  let expansion = if request == pkg_name {
    Cow::Borrowed(".")
  } else if let Some(slash_with_export) = request
    .strip_prefix(pkg_name)
    .filter(|t| t.starts_with('/'))
  {
    Cow::Owned(format!(".{}", slash_with_export))
  } else {
    return Ok(None);
  };

  if let Some(exports) = &pkg.exports {
    let node_resolver = state.borrow::<NodeResolverRc<
      TInNpmPackageChecker,
      TNpmPackageFolderResolver,
      TSys,
    >>();
    let referrer = UrlOrPathRef::from_path(&pkg.path);
    // invalidate the resolution cache in case things have changed
    NodeResolutionThreadLocalCache::clear();
    let r = node_resolver.package_exports_resolve(
      &pkg.path,
      &expansion,
      exports,
      Some(&referrer),
      ResolutionMode::Require,
      node_resolver.require_conditions(),
      NodeResolutionKind::Execution,
    )?;
    Ok(Some(url_or_path_to_string(r)?))
  } else {
    Ok(None)
  }
}

#[op2(stack_trace)]
pub fn op_require_read_file<TSys: ExtNodeSys + 'static>(
  state: &mut OpState,
  #[string] file_path_str: &str,
) -> Result<FastString, RequireError> {
  let file_path = Cow::Borrowed(Path::new(file_path_str));
  // todo(dsherret): there's multiple borrows to NodeRequireLoaderRc here
  let file_path = ensure_read_permission(state, file_path)
    .map_err(RequireErrorKind::Permission)?;
  let code = {
    let loader = state.borrow::<NodeRequireLoaderRc>();
    loader
      .load_text_file_lossy(&file_path)
      .map_err(|e| RequireErrorKind::ReadModule(e).into_box())?
  };
  // Apply load-time security mitigations for known React Server Components
  // CVEs to required (CommonJS) source. Opt in via `DENO_PATCH_REACT_CVE`.
  let sys = state.borrow::<TSys>();
  if deno_resolver::is_react_cve_patch_enabled(sys) {
    match deno_resolver::patch_react_cves(file_path_str, code.as_str().into()) {
      Cow::Borrowed(_) => Ok(code),
      Cow::Owned(s) => Ok(s.into()),
    }
  } else {
    Ok(code)
  }
}

#[op2]
#[string]
pub fn op_require_as_file_path(#[string] file_or_url: &str) -> Option<String> {
  #[allow(
    clippy::disallowed_methods,
    reason = "don't need error and this doesn't need to work in Wasm"
  )]
  if let Ok(url) = Url::parse(file_or_url)
    && let Ok(p) = url.to_file_path()
  {
    return Some(p.to_string_lossy().into_owned());
  }

  None // use original input
}

#[op2(stack_trace)]
#[string]
pub fn op_require_resolve_exports<
  TInNpmPackageChecker: InNpmPackageChecker + 'static,
  TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
  TSys: ExtNodeSys + 'static,
>(
  state: &mut OpState,
  uses_local_node_modules_dir: bool,
  #[string] modules_path_str: &str,
  #[string] _request: &str,
  #[string] name: &str,
  #[string] expansion: &str,
  #[string] parent_path: &str,
) -> Result<Option<String>, RequireError> {
  let sys = state.borrow::<TSys>();
  let node_resolver = state.borrow::<NodeResolverRc<
    TInNpmPackageChecker,
    TNpmPackageFolderResolver,
    TSys,
  >>();
  let pkg_json_resolver = state.borrow::<PackageJsonResolverRc<TSys>>();

  let modules_path = Path::new(&modules_path_str);
  let modules_specifier = deno_path_util::url_from_file_path(modules_path)?;
  let pkg_path = if node_resolver.in_npm_package(&modules_specifier)
    && !uses_local_node_modules_dir
  {
    Cow::Borrowed(modules_path)
  } else {
    let mod_dir = path_resolve([modules_path_str, name].into_iter());
    if sys.fs_is_dir_no_err(&mod_dir) {
      Cow::Owned(mod_dir)
    } else {
      Cow::Borrowed(modules_path)
    }
  };
  let Some(pkg) =
    pkg_json_resolver.load_package_json(&pkg_path.join("package.json"))?
  else {
    return Ok(None);
  };
  let Some(exports) = &pkg.exports else {
    return Ok(None);
  };

  let referrer = if parent_path.is_empty() {
    None
  } else {
    Some(PathBuf::from(parent_path))
  };
  NodeResolutionThreadLocalCache::clear();
  let r = node_resolver.package_exports_resolve(
    &pkg.path,
    &format!(".{expansion}"),
    exports,
    referrer
      .as_ref()
      .map(|r| UrlOrPathRef::from_path(r))
      .as_ref(),
    ResolutionMode::Require,
    node_resolver.require_conditions(),
    NodeResolutionKind::Execution,
  )?;
  Ok(Some(url_or_path_to_string(r)?))
}

deno_error::js_error_wrapper!(
  PackageJsonLoadError,
  JsPackageJsonLoadError,
  "Error"
);

#[op2(fast)]
pub fn op_require_is_maybe_cjs(
  state: &mut OpState,
  #[string] filename: &str,
) -> Result<bool, JsPackageJsonLoadError> {
  let filename = Path::new(filename);
  let Ok(url) = url_from_file_path(filename) else {
    return Ok(false);
  };
  let loader = state.borrow::<NodeRequireLoaderRc>();
  loader.is_maybe_cjs_from_require(&url).map_err(Into::into)
}

#[op2(stack_trace)]
#[serde]
pub fn op_require_read_package_scope<TSys: ExtNodeSys + 'static>(
  state: &mut OpState,
  #[string] package_json_path: &str,
) -> Option<PackageJsonRc> {
  let pkg_json_resolver = state.borrow::<PackageJsonResolverRc<TSys>>();
  let package_json_path = Path::new(package_json_path);
  if package_json_path.file_name() != Some("package.json".as_ref()) {
    // permissions: do not allow reading a non-package.json file
    return None;
  }
  pkg_json_resolver
    .load_package_json(package_json_path)
    .ok()
    .flatten()
}

#[op2(stack_trace)]
#[string]
pub fn op_require_package_imports_resolve<
  TInNpmPackageChecker: InNpmPackageChecker + 'static,
  TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
  TSys: ExtNodeSys + 'static,
>(
  state: &mut OpState,
  #[string] referrer_filename: &str,
  #[string] request: &str,
) -> Result<Option<String>, RequireError> {
  let referrer_path = Cow::Borrowed(Path::new(referrer_filename));
  let referrer_path = ensure_read_permission(state, referrer_path)
    .map_err(RequireErrorKind::Permission)?;
  let pkg_json_resolver = state.borrow::<PackageJsonResolverRc<TSys>>();
  let Some(pkg) = pkg_json_resolver.get_closest_package_json(&referrer_path)?
  else {
    return Ok(None);
  };

  if pkg.imports.is_some() {
    let node_resolver = state.borrow::<NodeResolverRc<
      TInNpmPackageChecker,
      TNpmPackageFolderResolver,
      TSys,
    >>();
    NodeResolutionThreadLocalCache::clear();
    let url = node_resolver.resolve_package_import(
      request,
      Some(&UrlOrPathRef::from_path(&referrer_path)),
      Some(&pkg),
      ResolutionMode::Require,
      NodeResolutionKind::Execution,
    )?;
    Ok(Some(url_or_path_to_string(url)?))
  } else {
    Ok(None)
  }
}

#[op2(fast, reentrant)]
pub fn op_require_break_on_next_statement(state: Rc<RefCell<OpState>>) {
  let inspector = { state.borrow().borrow::<Rc<JsRuntimeInspector>>().clone() };
  inspector.wait_for_session_and_break_on_next_statement()
}

#[op2(fast)]
pub fn op_require_can_parse_as_esm(
  scope: &mut v8::PinScope<'_, '_>,
  #[string] source: &str,
) -> bool {
  v8::tc_scope!(scope, scope);
  let Some(source) = v8::String::new(scope, source) else {
    return false;
  };
  let origin = v8::ScriptOrigin::new(
    scope,
    source.into(),
    0,
    0,
    false,
    0,
    None,
    true,
    false,
    true,
    None,
  );
  let mut source = v8::script_compiler::Source::new(source, Some(&origin));
  v8::script_compiler::compile_module(scope, &mut source).is_some()
}

fn url_or_path_to_string(
  url: UrlOrPath,
) -> Result<String, deno_path_util::UrlToFilePathError> {
  if url.is_file() {
    Ok(url.into_path()?.to_string_lossy().into_owned())
  } else {
    Ok(url.to_string_lossy().into_owned())
  }
}
