mirror of
https://github.com/denoland/deno.git
synced 2025-09-21 01:49:51 +00:00
1999 lines
58 KiB
Rust
1999 lines
58 KiB
Rust
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
|
|
mod flags;
|
|
mod flags_net;
|
|
|
|
use std::borrow::Cow;
|
|
use std::collections::HashMap;
|
|
use std::env;
|
|
use std::net::SocketAddr;
|
|
use std::num::NonZeroUsize;
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
use deno_ast::MediaType;
|
|
use deno_ast::ModuleSpecifier;
|
|
use deno_cache_dir::file_fetcher::CacheSetting;
|
|
pub use deno_config::deno_json::BenchConfig;
|
|
pub use deno_config::deno_json::CompilerOptions;
|
|
pub use deno_config::deno_json::ConfigFile;
|
|
use deno_config::deno_json::FmtConfig;
|
|
pub use deno_config::deno_json::FmtOptionsConfig;
|
|
pub use deno_config::deno_json::LintRulesConfig;
|
|
use deno_config::deno_json::NodeModulesDirMode;
|
|
use deno_config::deno_json::PermissionConfigValue;
|
|
use deno_config::deno_json::PermissionsObjectWithBase;
|
|
pub use deno_config::deno_json::ProseWrap;
|
|
use deno_config::deno_json::TestConfig;
|
|
pub use deno_config::glob::FilePatterns;
|
|
pub use deno_config::workspace::TsTypeLib;
|
|
use deno_config::workspace::Workspace;
|
|
use deno_config::workspace::WorkspaceDirLintConfig;
|
|
use deno_config::workspace::WorkspaceDirectory;
|
|
use deno_config::workspace::WorkspaceDirectoryRc;
|
|
use deno_config::workspace::WorkspaceLintConfig;
|
|
use deno_core::anyhow::bail;
|
|
use deno_core::error::AnyError;
|
|
use deno_core::url::Url;
|
|
use deno_graph::GraphKind;
|
|
use deno_lib::args::CaData;
|
|
use deno_lib::args::has_flag_env_var;
|
|
use deno_lib::args::npm_pkg_req_ref_to_binary_command;
|
|
use deno_lib::args::npm_process_state;
|
|
use deno_lib::version::DENO_VERSION_INFO;
|
|
use deno_lib::worker::StorageKeyResolver;
|
|
use deno_npm::NpmSystemInfo;
|
|
use deno_npm_installer::LifecycleScriptsConfig;
|
|
use deno_npm_installer::graph::NpmCachingStrategy;
|
|
use deno_path_util::resolve_url_or_path;
|
|
use deno_resolver::factory::resolve_jsr_url;
|
|
use deno_runtime::deno_permissions::AllowRunDescriptor;
|
|
use deno_runtime::deno_permissions::PathDescriptor;
|
|
use deno_runtime::deno_permissions::PermissionsOptions;
|
|
use deno_runtime::inspector_server::InspectorServer;
|
|
use deno_semver::StackString;
|
|
use deno_semver::npm::NpmPackageReqReference;
|
|
use deno_telemetry::OtelConfig;
|
|
use deno_terminal::colors;
|
|
pub use flags::*;
|
|
use once_cell::sync::Lazy;
|
|
use thiserror::Error;
|
|
|
|
use crate::sys::CliSys;
|
|
|
|
pub type CliLockfile = deno_resolver::lockfile::LockfileLock<CliSys>;
|
|
|
|
pub fn jsr_url() -> &'static Url {
|
|
static JSR_URL: Lazy<Url> = Lazy::new(|| resolve_jsr_url(&CliSys::default()));
|
|
|
|
&JSR_URL
|
|
}
|
|
|
|
pub fn jsr_api_url() -> &'static Url {
|
|
static JSR_API_URL: Lazy<Url> = Lazy::new(|| {
|
|
let mut jsr_api_url = jsr_url().clone();
|
|
jsr_api_url.set_path("api/");
|
|
jsr_api_url
|
|
});
|
|
|
|
&JSR_API_URL
|
|
}
|
|
|
|
pub struct WorkspaceBenchOptions {
|
|
pub filter: Option<String>,
|
|
pub json: bool,
|
|
pub no_run: bool,
|
|
pub permit_no_files: bool,
|
|
}
|
|
|
|
impl WorkspaceBenchOptions {
|
|
pub fn resolve(bench_flags: &BenchFlags) -> Self {
|
|
Self {
|
|
filter: bench_flags.filter.clone(),
|
|
json: bench_flags.json,
|
|
no_run: bench_flags.no_run,
|
|
permit_no_files: bench_flags.permit_no_files,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct BenchOptions {
|
|
pub files: FilePatterns,
|
|
}
|
|
|
|
impl BenchOptions {
|
|
pub fn resolve(bench_config: BenchConfig, _bench_flags: &BenchFlags) -> Self {
|
|
// this is the same, but keeping the same pattern as everywhere else for the future
|
|
Self {
|
|
files: bench_config.files,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
|
pub struct UnstableFmtOptions {
|
|
pub component: bool,
|
|
pub sql: bool,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct FmtOptions {
|
|
pub options: FmtOptionsConfig,
|
|
pub unstable: UnstableFmtOptions,
|
|
pub files: FilePatterns,
|
|
}
|
|
|
|
impl Default for FmtOptions {
|
|
fn default() -> Self {
|
|
Self::new_with_base(PathBuf::from("/"))
|
|
}
|
|
}
|
|
|
|
impl FmtOptions {
|
|
pub fn new_with_base(base: PathBuf) -> Self {
|
|
Self {
|
|
options: FmtOptionsConfig::default(),
|
|
unstable: Default::default(),
|
|
files: FilePatterns::new_with_base(base),
|
|
}
|
|
}
|
|
|
|
pub fn resolve(
|
|
fmt_config: FmtConfig,
|
|
unstable: UnstableFmtOptions,
|
|
fmt_flags: &FmtFlags,
|
|
) -> Self {
|
|
Self {
|
|
options: resolve_fmt_options(fmt_flags, fmt_config.options),
|
|
unstable: UnstableFmtOptions {
|
|
component: unstable.component || fmt_flags.unstable_component,
|
|
sql: unstable.sql || fmt_flags.unstable_sql,
|
|
},
|
|
files: fmt_config.files,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn resolve_fmt_options(
|
|
fmt_flags: &FmtFlags,
|
|
mut options: FmtOptionsConfig,
|
|
) -> FmtOptionsConfig {
|
|
if let Some(use_tabs) = fmt_flags.use_tabs {
|
|
options.use_tabs = Some(use_tabs);
|
|
}
|
|
|
|
if let Some(line_width) = fmt_flags.line_width {
|
|
options.line_width = Some(line_width.get());
|
|
}
|
|
|
|
if let Some(indent_width) = fmt_flags.indent_width {
|
|
options.indent_width = Some(indent_width.get());
|
|
}
|
|
|
|
if let Some(single_quote) = fmt_flags.single_quote {
|
|
options.single_quote = Some(single_quote);
|
|
}
|
|
|
|
if let Some(prose_wrap) = &fmt_flags.prose_wrap {
|
|
options.prose_wrap = Some(match prose_wrap.as_str() {
|
|
"always" => ProseWrap::Always,
|
|
"never" => ProseWrap::Never,
|
|
"preserve" => ProseWrap::Preserve,
|
|
// validators in `flags.rs` makes other values unreachable
|
|
_ => unreachable!(),
|
|
});
|
|
}
|
|
|
|
if let Some(no_semis) = &fmt_flags.no_semicolons {
|
|
options.semi_colons = Some(!no_semis);
|
|
}
|
|
|
|
options
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct WorkspaceTestOptions {
|
|
pub doc: bool,
|
|
pub no_run: bool,
|
|
pub fail_fast: Option<NonZeroUsize>,
|
|
pub permit_no_files: bool,
|
|
pub filter: Option<String>,
|
|
pub shuffle: Option<u64>,
|
|
pub concurrent_jobs: NonZeroUsize,
|
|
pub trace_leaks: bool,
|
|
pub reporter: TestReporterConfig,
|
|
pub junit_path: Option<String>,
|
|
pub hide_stacktraces: bool,
|
|
}
|
|
|
|
impl WorkspaceTestOptions {
|
|
pub fn resolve(test_flags: &TestFlags) -> Self {
|
|
Self {
|
|
permit_no_files: test_flags.permit_no_files,
|
|
concurrent_jobs: parallelism_count(test_flags.parallel),
|
|
doc: test_flags.doc,
|
|
fail_fast: test_flags.fail_fast,
|
|
filter: test_flags.filter.clone(),
|
|
no_run: test_flags.no_run,
|
|
shuffle: test_flags.shuffle,
|
|
trace_leaks: test_flags.trace_leaks,
|
|
reporter: test_flags.reporter,
|
|
junit_path: test_flags.junit_path.clone(),
|
|
hide_stacktraces: test_flags.hide_stacktraces,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct TestOptions {
|
|
pub files: FilePatterns,
|
|
}
|
|
|
|
impl TestOptions {
|
|
pub fn resolve(test_config: TestConfig, _test_flags: &TestFlags) -> Self {
|
|
// this is the same, but keeping the same pattern as everywhere else for the future
|
|
Self {
|
|
files: test_config.files,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Default, Debug)]
|
|
pub enum LintReporterKind {
|
|
#[default]
|
|
Pretty,
|
|
Json,
|
|
Compact,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct WorkspaceLintOptions {
|
|
pub reporter_kind: LintReporterKind,
|
|
}
|
|
|
|
impl WorkspaceLintOptions {
|
|
pub fn resolve(
|
|
lint_config: &WorkspaceLintConfig,
|
|
lint_flags: &LintFlags,
|
|
) -> Result<Self, AnyError> {
|
|
let mut maybe_reporter_kind = if lint_flags.json {
|
|
Some(LintReporterKind::Json)
|
|
} else if lint_flags.compact {
|
|
Some(LintReporterKind::Compact)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
if maybe_reporter_kind.is_none() {
|
|
// Flag not set, so try to get lint reporter from the config file.
|
|
maybe_reporter_kind = match lint_config.report.as_deref() {
|
|
Some("json") => Some(LintReporterKind::Json),
|
|
Some("compact") => Some(LintReporterKind::Compact),
|
|
Some("pretty") => Some(LintReporterKind::Pretty),
|
|
Some(_) => {
|
|
bail!("Invalid lint report type in config file")
|
|
}
|
|
None => None,
|
|
}
|
|
}
|
|
Ok(Self {
|
|
reporter_kind: maybe_reporter_kind.unwrap_or_default(),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct LintOptions {
|
|
pub rules: LintRulesConfig,
|
|
pub files: FilePatterns,
|
|
pub fix: bool,
|
|
pub plugins: Vec<Url>,
|
|
}
|
|
|
|
impl Default for LintOptions {
|
|
fn default() -> Self {
|
|
Self::new_with_base(PathBuf::from("/"))
|
|
}
|
|
}
|
|
|
|
impl LintOptions {
|
|
pub fn new_with_base(base: PathBuf) -> Self {
|
|
Self {
|
|
rules: Default::default(),
|
|
files: FilePatterns::new_with_base(base),
|
|
fix: false,
|
|
plugins: vec![],
|
|
}
|
|
}
|
|
|
|
pub fn resolve(
|
|
lint_config: WorkspaceDirLintConfig,
|
|
lint_flags: &LintFlags,
|
|
) -> Result<Self, AnyError> {
|
|
let rules = resolve_lint_rules_options(
|
|
lint_config.rules,
|
|
lint_flags.maybe_rules_tags.clone(),
|
|
lint_flags.maybe_rules_include.clone(),
|
|
lint_flags.maybe_rules_exclude.clone(),
|
|
);
|
|
|
|
let mut plugins = lint_config.plugins;
|
|
plugins.sort_unstable();
|
|
|
|
Ok(Self {
|
|
files: lint_config.files,
|
|
rules,
|
|
fix: lint_flags.fix,
|
|
plugins,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn resolve_lint_rules_options(
|
|
config_rules: LintRulesConfig,
|
|
mut maybe_rules_tags: Option<Vec<String>>,
|
|
mut maybe_rules_include: Option<Vec<String>>,
|
|
mut maybe_rules_exclude: Option<Vec<String>>,
|
|
) -> LintRulesConfig {
|
|
// Try to get configured rules. CLI flags take precedence
|
|
// over config file, i.e. if there's `rules.include` in config file
|
|
// and `--rules-include` CLI flag, only the flag value is taken into account.
|
|
if maybe_rules_include.is_none() {
|
|
maybe_rules_include = config_rules.include;
|
|
}
|
|
if maybe_rules_exclude.is_none() {
|
|
maybe_rules_exclude = config_rules.exclude;
|
|
}
|
|
if maybe_rules_tags.is_none() {
|
|
maybe_rules_tags = config_rules.tags;
|
|
}
|
|
|
|
LintRulesConfig {
|
|
exclude: maybe_rules_exclude,
|
|
include: maybe_rules_include,
|
|
tags: maybe_rules_tags,
|
|
}
|
|
}
|
|
|
|
pub struct WorkspaceMainModuleResolver {
|
|
workspace_resolver: Arc<deno_resolver::workspace::WorkspaceResolver<CliSys>>,
|
|
node_resolver: Arc<crate::node::CliNodeResolver>,
|
|
}
|
|
|
|
impl WorkspaceMainModuleResolver {
|
|
pub fn new(
|
|
workspace_resolver: Arc<
|
|
deno_resolver::workspace::WorkspaceResolver<CliSys>,
|
|
>,
|
|
node_resolver: Arc<crate::node::CliNodeResolver>,
|
|
) -> Self {
|
|
Self {
|
|
workspace_resolver,
|
|
node_resolver,
|
|
}
|
|
}
|
|
}
|
|
impl WorkspaceMainModuleResolver {
|
|
fn resolve_main_module(
|
|
&self,
|
|
specifier: &str,
|
|
cwd: &Url,
|
|
) -> Result<Url, AnyError> {
|
|
let resolution = self.workspace_resolver.resolve(
|
|
specifier,
|
|
cwd,
|
|
deno_resolver::workspace::ResolutionKind::Execution,
|
|
)?;
|
|
let url = match resolution {
|
|
deno_resolver::workspace::MappedResolution::Normal {
|
|
specifier, ..
|
|
} => specifier,
|
|
deno_resolver::workspace::MappedResolution::WorkspaceJsrPackage {
|
|
specifier,
|
|
..
|
|
} => specifier,
|
|
deno_resolver::workspace::MappedResolution::WorkspaceNpmPackage {
|
|
target_pkg_json,
|
|
sub_path,
|
|
..
|
|
} => self
|
|
.node_resolver
|
|
.resolve_package_subpath_from_deno_module(
|
|
target_pkg_json.clone().dir_path(),
|
|
sub_path.as_deref(),
|
|
Some(cwd),
|
|
node_resolver::ResolutionMode::Import,
|
|
node_resolver::NodeResolutionKind::Execution,
|
|
)?
|
|
.into_url()?,
|
|
deno_resolver::workspace::MappedResolution::PackageJson {
|
|
sub_path,
|
|
dep_result,
|
|
alias,
|
|
..
|
|
} => {
|
|
let result = dep_result
|
|
.as_ref()
|
|
.map_err(|e| deno_core::anyhow::anyhow!("{e}"))?;
|
|
match result {
|
|
deno_package_json::PackageJsonDepValue::File(file) => {
|
|
let cwd_path = deno_path_util::url_to_file_path(cwd)?;
|
|
deno_path_util::resolve_path(file, &cwd_path)?
|
|
}
|
|
deno_package_json::PackageJsonDepValue::Req(package_req) => {
|
|
ModuleSpecifier::parse(&format!(
|
|
"npm:{}{}",
|
|
package_req,
|
|
sub_path.map(|s| format!("/{}", s)).unwrap_or_default()
|
|
))?
|
|
}
|
|
deno_package_json::PackageJsonDepValue::Workspace(version_req) => {
|
|
let pkg_folder = self
|
|
.workspace_resolver
|
|
.resolve_workspace_pkg_json_folder_for_pkg_json_dep(
|
|
alias,
|
|
version_req,
|
|
)?;
|
|
self
|
|
.node_resolver
|
|
.resolve_package_subpath_from_deno_module(
|
|
pkg_folder,
|
|
sub_path.as_deref(),
|
|
Some(cwd),
|
|
node_resolver::ResolutionMode::Import,
|
|
node_resolver::NodeResolutionKind::Execution,
|
|
)?
|
|
.into_url()?
|
|
}
|
|
deno_package_json::PackageJsonDepValue::JsrReq(_) => {
|
|
return Err(
|
|
deno_resolver::DenoResolveErrorKind::UnsupportedPackageJsonJsrReq
|
|
.into_box()
|
|
.into(),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
deno_resolver::workspace::MappedResolution::PackageJsonImport {
|
|
pkg_json,
|
|
} => self
|
|
.node_resolver
|
|
.resolve_package_import(
|
|
specifier,
|
|
Some(&node_resolver::UrlOrPathRef::from_url(cwd)),
|
|
Some(pkg_json),
|
|
node_resolver::ResolutionMode::Import,
|
|
node_resolver::NodeResolutionKind::Execution,
|
|
)?
|
|
.into_url()?,
|
|
};
|
|
Ok(url)
|
|
}
|
|
}
|
|
|
|
/// Holds the resolved options of many sources used by subcommands
|
|
/// and provides some helper function for creating common objects.
|
|
#[derive(Debug)]
|
|
pub struct CliOptions {
|
|
// the source of the options is a detail the rest of the
|
|
// application need not concern itself with, so keep these private
|
|
flags: Arc<Flags>,
|
|
initial_cwd: PathBuf,
|
|
main_module_cell: std::sync::OnceLock<Result<ModuleSpecifier, AnyError>>,
|
|
pub start_dir: Arc<WorkspaceDirectory>,
|
|
}
|
|
|
|
impl CliOptions {
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
flags: Arc<Flags>,
|
|
initial_cwd: PathBuf,
|
|
start_dir: Arc<WorkspaceDirectory>,
|
|
) -> Result<Self, AnyError> {
|
|
if let Some(insecure_allowlist) =
|
|
flags.unsafely_ignore_certificate_errors.as_ref()
|
|
{
|
|
let domains = if insecure_allowlist.is_empty() {
|
|
"for all hostnames".to_string()
|
|
} else {
|
|
format!("for: {}", insecure_allowlist.join(", "))
|
|
};
|
|
let msg =
|
|
format!("DANGER: TLS certificate validation is disabled {}", domains);
|
|
{
|
|
log::error!("{}", colors::yellow(msg));
|
|
}
|
|
}
|
|
|
|
Ok(Self {
|
|
flags,
|
|
initial_cwd,
|
|
main_module_cell: std::sync::OnceLock::new(),
|
|
start_dir,
|
|
})
|
|
}
|
|
|
|
pub fn from_flags(
|
|
flags: Arc<Flags>,
|
|
initial_cwd: PathBuf,
|
|
start_dir: Arc<WorkspaceDirectory>,
|
|
) -> Result<Self, AnyError> {
|
|
for diagnostic in start_dir.workspace.diagnostics() {
|
|
log::warn!("{} {}", colors::yellow("Warning"), diagnostic);
|
|
}
|
|
|
|
log::debug!("Finished config loading.");
|
|
|
|
Self::new(flags, initial_cwd, start_dir)
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn initial_cwd(&self) -> &Path {
|
|
&self.initial_cwd
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn workspace(&self) -> &Arc<Workspace> {
|
|
&self.start_dir.workspace
|
|
}
|
|
|
|
pub fn graph_kind(&self) -> GraphKind {
|
|
match self.sub_command() {
|
|
DenoSubcommand::Cache(_) => GraphKind::All,
|
|
DenoSubcommand::Check(_) => GraphKind::TypesOnly,
|
|
DenoSubcommand::Install(InstallFlags::Local(_)) => GraphKind::All,
|
|
_ => self.type_check_mode().as_graph_kind(),
|
|
}
|
|
}
|
|
|
|
pub fn ts_type_lib_window(&self) -> TsTypeLib {
|
|
TsTypeLib::DenoWindow
|
|
}
|
|
|
|
pub fn ts_type_lib_worker(&self) -> TsTypeLib {
|
|
TsTypeLib::DenoWorker
|
|
}
|
|
|
|
pub fn cache_setting(&self) -> CacheSetting {
|
|
if self.flags.cached_only {
|
|
CacheSetting::Only
|
|
} else if !self.flags.cache_blocklist.is_empty() {
|
|
CacheSetting::ReloadSome(self.flags.cache_blocklist.clone())
|
|
} else if self.flags.reload {
|
|
CacheSetting::ReloadAll
|
|
} else {
|
|
CacheSetting::Use
|
|
}
|
|
}
|
|
|
|
pub fn npm_system_info(&self) -> NpmSystemInfo {
|
|
self.sub_command().npm_system_info()
|
|
}
|
|
|
|
/// Resolve the specifier for a specified import map.
|
|
///
|
|
/// This will NOT include the config file if it
|
|
/// happens to be an import map.
|
|
pub fn resolve_specified_import_map_specifier(
|
|
&self,
|
|
) -> Result<Option<ModuleSpecifier>, ImportMapSpecifierResolveError> {
|
|
resolve_import_map_specifier(
|
|
self.flags.import_map_path.as_deref(),
|
|
self.workspace().root_deno_json().map(|c| c.as_ref()),
|
|
&self.initial_cwd,
|
|
)
|
|
}
|
|
|
|
pub fn node_ipc_fd(&self) -> Option<i64> {
|
|
let maybe_node_channel_fd = std::env::var("NODE_CHANNEL_FD").ok();
|
|
if let Some(node_channel_fd) = maybe_node_channel_fd {
|
|
// Remove so that child processes don't inherit this environment variable.
|
|
|
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
|
unsafe {
|
|
std::env::remove_var("NODE_CHANNEL_FD")
|
|
};
|
|
node_channel_fd.parse::<i64>().ok()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn serve_port(&self) -> Option<u16> {
|
|
if let DenoSubcommand::Serve(flags) = self.sub_command() {
|
|
Some(flags.port)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn serve_host(&self) -> Option<String> {
|
|
if let DenoSubcommand::Serve(flags) = self.sub_command() {
|
|
Some(flags.host.clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn eszip(&self) -> bool {
|
|
self.flags.eszip
|
|
}
|
|
|
|
pub fn node_conditions(&self) -> &[String] {
|
|
self.flags.node_conditions.as_ref()
|
|
}
|
|
|
|
pub fn otel_config(&self) -> OtelConfig {
|
|
self.flags.otel_config()
|
|
}
|
|
|
|
pub fn no_legacy_abort(&self) -> bool {
|
|
self.flags.no_legacy_abort()
|
|
}
|
|
|
|
pub fn env_file_name(&self) -> Option<&Vec<String>> {
|
|
self.flags.env_file.as_ref()
|
|
}
|
|
|
|
pub fn preload_modules(&self) -> Result<Vec<ModuleSpecifier>, AnyError> {
|
|
if self.flags.preload.is_empty() {
|
|
return Ok(vec![]);
|
|
}
|
|
|
|
let mut preload = Vec::with_capacity(self.flags.preload.len());
|
|
for preload_specifier in self.flags.preload.iter() {
|
|
preload.push(resolve_url_or_path(preload_specifier, self.initial_cwd())?);
|
|
}
|
|
|
|
Ok(preload)
|
|
}
|
|
|
|
fn resolve_main_module_with_resolver_if_bare(
|
|
&self,
|
|
raw_specifier: &str,
|
|
resolver: Option<&WorkspaceMainModuleResolver>,
|
|
default_resolve: impl Fn() -> Result<ModuleSpecifier, AnyError>,
|
|
) -> Result<ModuleSpecifier, AnyError> {
|
|
match resolver {
|
|
Some(resolver)
|
|
if !raw_specifier.starts_with('.')
|
|
&& !Path::new(raw_specifier).is_absolute() =>
|
|
{
|
|
let cwd = deno_path_util::url_from_directory_path(self.initial_cwd())?;
|
|
resolver
|
|
.resolve_main_module(raw_specifier, &cwd)
|
|
.or_else(|_| default_resolve())
|
|
}
|
|
_ => default_resolve(),
|
|
}
|
|
}
|
|
|
|
pub fn resolve_main_module_with_resolver(
|
|
&self,
|
|
resolver: Option<&WorkspaceMainModuleResolver>,
|
|
) -> Result<&ModuleSpecifier, AnyError> {
|
|
self
|
|
.main_module_cell
|
|
.get_or_init(|| {
|
|
Ok(match &self.flags.subcommand {
|
|
DenoSubcommand::Compile(compile_flags) => {
|
|
resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
|
|
}
|
|
DenoSubcommand::Eval(_) => {
|
|
let specifier = format!(
|
|
"./$deno$eval.{}",
|
|
self.flags.ext.as_deref().unwrap_or("mts")
|
|
);
|
|
deno_path_util::resolve_path(&specifier, self.initial_cwd())?
|
|
}
|
|
DenoSubcommand::Repl(_) => deno_path_util::resolve_path(
|
|
"./$deno$repl.mts",
|
|
self.initial_cwd(),
|
|
)?,
|
|
DenoSubcommand::Run(run_flags) => {
|
|
if run_flags.is_stdin() {
|
|
let specifier = format!(
|
|
"./$deno$stdin.{}",
|
|
self.flags.ext.as_deref().unwrap_or("mts")
|
|
);
|
|
deno_path_util::resolve_path(&specifier, self.initial_cwd())?
|
|
} else {
|
|
let default_resolve = || {
|
|
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
|
|
{
|
|
Ok::<_, AnyError>(
|
|
try_resolve_node_binary_main_entrypoint(
|
|
&run_flags.script,
|
|
self.initial_cwd(),
|
|
)?
|
|
.unwrap_or(url),
|
|
)
|
|
} else {
|
|
Ok(url)
|
|
}
|
|
};
|
|
self.resolve_main_module_with_resolver_if_bare(
|
|
&run_flags.script,
|
|
resolver,
|
|
default_resolve,
|
|
)?
|
|
}
|
|
}
|
|
DenoSubcommand::Serve(run_flags) => self
|
|
.resolve_main_module_with_resolver_if_bare(
|
|
&run_flags.script,
|
|
resolver,
|
|
|| {
|
|
resolve_url_or_path(&run_flags.script, self.initial_cwd())
|
|
.map_err(|e| e.into())
|
|
},
|
|
)?,
|
|
_ => {
|
|
bail!("No main module.")
|
|
}
|
|
})
|
|
})
|
|
.as_ref()
|
|
.map_err(|err| deno_core::anyhow::anyhow!("{}", err))
|
|
}
|
|
|
|
pub fn resolve_main_module(&self) -> Result<&ModuleSpecifier, AnyError> {
|
|
self.resolve_main_module_with_resolver(None)
|
|
}
|
|
|
|
pub fn resolve_file_header_overrides(
|
|
&self,
|
|
) -> HashMap<ModuleSpecifier, HashMap<String, String>> {
|
|
let maybe_main_specifier = self.resolve_main_module().ok();
|
|
let maybe_content_type = self.flags.ext.as_ref().and_then(|ext| {
|
|
let media_type = MediaType::from_filename(&format!("file.{}", ext));
|
|
media_type.as_content_type()
|
|
});
|
|
|
|
if let (Some(main_specifier), Some(content_type)) =
|
|
(maybe_main_specifier, maybe_content_type)
|
|
{
|
|
HashMap::from([(
|
|
main_specifier.clone(),
|
|
HashMap::from([("content-type".to_string(), content_type.to_string())]),
|
|
)])
|
|
} else {
|
|
HashMap::default()
|
|
}
|
|
}
|
|
|
|
pub fn resolve_storage_key_resolver(&self) -> StorageKeyResolver {
|
|
if let Some(location) = &self.flags.location {
|
|
StorageKeyResolver::from_flag(location)
|
|
} else if let Some(deno_json) = self.start_dir.maybe_deno_json() {
|
|
StorageKeyResolver::from_config_file_url(&deno_json.specifier)
|
|
} else {
|
|
StorageKeyResolver::new_use_main_module()
|
|
}
|
|
}
|
|
|
|
// If the main module should be treated as being in an npm package.
|
|
// 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_node_main(&self) -> bool {
|
|
npm_process_state(&CliSys::default()).is_some()
|
|
}
|
|
|
|
/// Gets the explicitly specified NodeModulesDir setting.
|
|
///
|
|
/// Use `WorkspaceFactory.node_modules_dir_mode()` to get the resolved value.
|
|
pub fn specified_node_modules_dir(
|
|
&self,
|
|
) -> Result<
|
|
Option<NodeModulesDirMode>,
|
|
deno_config::deno_json::NodeModulesDirParseError,
|
|
> {
|
|
if let Some(flag) = self.flags.node_modules_dir {
|
|
return Ok(Some(flag));
|
|
}
|
|
self.workspace().node_modules_dir()
|
|
}
|
|
|
|
pub fn vendor_dir_path(&self) -> Option<&PathBuf> {
|
|
self.workspace().vendor_dir_path()
|
|
}
|
|
|
|
pub fn resolve_inspector_server(
|
|
&self,
|
|
) -> Result<Option<InspectorServer>, AnyError> {
|
|
let maybe_inspect_host = self
|
|
.flags
|
|
.inspect
|
|
.or(self.flags.inspect_brk)
|
|
.or(self.flags.inspect_wait);
|
|
|
|
let Some(host) = maybe_inspect_host else {
|
|
return Ok(None);
|
|
};
|
|
|
|
Ok(Some(InspectorServer::new(
|
|
host,
|
|
DENO_VERSION_INFO.user_agent,
|
|
)?))
|
|
}
|
|
|
|
pub fn resolve_fmt_options_for_members(
|
|
&self,
|
|
fmt_flags: &FmtFlags,
|
|
) -> Result<Vec<(WorkspaceDirectoryRc, FmtOptions)>, AnyError> {
|
|
let cli_arg_patterns =
|
|
fmt_flags.files.as_file_patterns(self.initial_cwd())?;
|
|
let member_configs = self
|
|
.workspace()
|
|
.resolve_fmt_config_for_members(&cli_arg_patterns)?;
|
|
let unstable = self.resolve_config_unstable_fmt_options();
|
|
let mut result = Vec::with_capacity(member_configs.len());
|
|
for (ctx, config) in member_configs {
|
|
let options = FmtOptions::resolve(config, unstable.clone(), fmt_flags);
|
|
result.push((ctx, options));
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
pub fn resolve_config_unstable_fmt_options(&self) -> UnstableFmtOptions {
|
|
let workspace = self.workspace();
|
|
UnstableFmtOptions {
|
|
component: workspace.has_unstable("fmt-component"),
|
|
sql: workspace.has_unstable("fmt-sql"),
|
|
}
|
|
}
|
|
|
|
pub fn resolve_workspace_lint_options(
|
|
&self,
|
|
lint_flags: &LintFlags,
|
|
) -> Result<WorkspaceLintOptions, AnyError> {
|
|
let lint_config = self.workspace().to_lint_config()?;
|
|
WorkspaceLintOptions::resolve(&lint_config, lint_flags)
|
|
}
|
|
|
|
pub fn resolve_lint_options_for_members(
|
|
&self,
|
|
lint_flags: &LintFlags,
|
|
) -> Result<Vec<(WorkspaceDirectoryRc, LintOptions)>, AnyError> {
|
|
let cli_arg_patterns =
|
|
lint_flags.files.as_file_patterns(self.initial_cwd())?;
|
|
let member_configs = self
|
|
.workspace()
|
|
.resolve_lint_config_for_members(&cli_arg_patterns)?;
|
|
let mut result = Vec::with_capacity(member_configs.len());
|
|
for (ctx, config) in member_configs {
|
|
let options = LintOptions::resolve(config, lint_flags)?;
|
|
result.push((ctx, options));
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
pub fn resolve_workspace_test_options(
|
|
&self,
|
|
test_flags: &TestFlags,
|
|
) -> WorkspaceTestOptions {
|
|
WorkspaceTestOptions::resolve(test_flags)
|
|
}
|
|
|
|
pub fn resolve_test_options_for_members(
|
|
&self,
|
|
test_flags: &TestFlags,
|
|
) -> Result<Vec<(WorkspaceDirectoryRc, TestOptions)>, AnyError> {
|
|
let cli_arg_patterns =
|
|
test_flags.files.as_file_patterns(self.initial_cwd())?;
|
|
let workspace_dir_configs = self
|
|
.workspace()
|
|
.resolve_test_config_for_members(&cli_arg_patterns)?;
|
|
let mut result = Vec::with_capacity(workspace_dir_configs.len());
|
|
for (member_dir, config) in workspace_dir_configs {
|
|
let options = TestOptions::resolve(config, test_flags);
|
|
result.push((member_dir, options));
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
pub fn resolve_workspace_bench_options(
|
|
&self,
|
|
bench_flags: &BenchFlags,
|
|
) -> WorkspaceBenchOptions {
|
|
WorkspaceBenchOptions::resolve(bench_flags)
|
|
}
|
|
|
|
pub fn resolve_bench_options_for_members(
|
|
&self,
|
|
bench_flags: &BenchFlags,
|
|
) -> Result<Vec<(WorkspaceDirectoryRc, BenchOptions)>, AnyError> {
|
|
let cli_arg_patterns =
|
|
bench_flags.files.as_file_patterns(self.initial_cwd())?;
|
|
let workspace_dir_configs = self
|
|
.workspace()
|
|
.resolve_bench_config_for_members(&cli_arg_patterns)?;
|
|
let mut result = Vec::with_capacity(workspace_dir_configs.len());
|
|
for (member_dir, config) in workspace_dir_configs {
|
|
let options = BenchOptions::resolve(config, bench_flags);
|
|
result.push((member_dir, options));
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
/// Vector of user script CLI arguments.
|
|
pub fn argv(&self) -> &Vec<String> {
|
|
&self.flags.argv
|
|
}
|
|
|
|
pub fn ca_data(&self) -> &Option<CaData> {
|
|
&self.flags.ca_data
|
|
}
|
|
|
|
pub fn ca_stores(&self) -> &Option<Vec<String>> {
|
|
&self.flags.ca_stores
|
|
}
|
|
|
|
pub fn coverage_dir(&self) -> Option<PathBuf> {
|
|
match &self.flags.subcommand {
|
|
DenoSubcommand::Test(test) => test
|
|
.coverage_dir
|
|
.as_ref()
|
|
.map(|dir| self.initial_cwd.join(dir))
|
|
.or_else(|| env::var_os("DENO_COVERAGE_DIR").map(PathBuf::from)),
|
|
DenoSubcommand::Run(flags) => flags
|
|
.coverage_dir
|
|
.as_ref()
|
|
.map(|dir| self.initial_cwd.join(dir))
|
|
.or_else(|| env::var_os("DENO_COVERAGE_DIR").map(PathBuf::from)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn enable_op_summary_metrics(&self) -> bool {
|
|
self.flags.enable_op_summary_metrics
|
|
|| matches!(
|
|
self.flags.subcommand,
|
|
DenoSubcommand::Test(_)
|
|
| DenoSubcommand::Repl(_)
|
|
| DenoSubcommand::Jupyter(_)
|
|
)
|
|
}
|
|
|
|
pub fn enable_testing_features(&self) -> bool {
|
|
self.flags.enable_testing_features
|
|
}
|
|
|
|
pub fn ext_flag(&self) -> &Option<String> {
|
|
&self.flags.ext
|
|
}
|
|
|
|
pub fn has_hmr(&self) -> bool {
|
|
if let DenoSubcommand::Run(RunFlags {
|
|
watch: Some(WatchFlagsWithPaths { hmr, .. }),
|
|
..
|
|
}) = &self.flags.subcommand
|
|
{
|
|
*hmr
|
|
} else if let DenoSubcommand::Serve(ServeFlags {
|
|
watch: Some(WatchFlagsWithPaths { hmr, .. }),
|
|
..
|
|
}) = &self.flags.subcommand
|
|
{
|
|
*hmr
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
/// If the --inspect or --inspect-brk flags are used.
|
|
pub fn is_inspecting(&self) -> bool {
|
|
self.flags.inspect.is_some()
|
|
|| self.flags.inspect_brk.is_some()
|
|
|| self.flags.inspect_wait.is_some()
|
|
}
|
|
|
|
pub fn inspect_brk(&self) -> Option<SocketAddr> {
|
|
self.flags.inspect_brk
|
|
}
|
|
|
|
pub fn inspect_wait(&self) -> Option<SocketAddr> {
|
|
self.flags.inspect_wait
|
|
}
|
|
|
|
pub fn log_level(&self) -> Option<log::Level> {
|
|
self.flags.log_level
|
|
}
|
|
|
|
pub fn is_quiet(&self) -> bool {
|
|
self
|
|
.log_level()
|
|
.map(|l| l == log::Level::Error)
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
pub fn location_flag(&self) -> &Option<Url> {
|
|
&self.flags.location
|
|
}
|
|
|
|
pub fn no_remote(&self) -> bool {
|
|
self.flags.no_remote
|
|
}
|
|
|
|
pub fn permissions_options(&self) -> Result<PermissionsOptions, AnyError> {
|
|
self.permissions_options_for_dir(&self.start_dir)
|
|
}
|
|
|
|
pub fn permissions_options_for_dir(
|
|
&self,
|
|
dir: &WorkspaceDirectory,
|
|
) -> Result<PermissionsOptions, AnyError> {
|
|
let config_permissions = self.resolve_config_permissions_for_dir(dir)?;
|
|
let mut permissions_options = flags_to_permissions_options(
|
|
&self.flags.permissions,
|
|
config_permissions,
|
|
)?;
|
|
self.augment_import_permissions(&mut permissions_options);
|
|
Ok(permissions_options)
|
|
}
|
|
|
|
fn resolve_config_permissions_for_dir<'a>(
|
|
&self,
|
|
dir: &'a WorkspaceDirectory,
|
|
) -> Result<Option<&'a PermissionsObjectWithBase>, AnyError> {
|
|
let config_permissions = if let Some(name) = &self.flags.permission_set {
|
|
if name.is_empty() {
|
|
let maybe_subcommand_permissions = match &self.flags.subcommand {
|
|
DenoSubcommand::Bench(_) => dir.to_bench_permissions_config()?,
|
|
DenoSubcommand::Compile(_) => dir.to_compile_permissions_config()?,
|
|
DenoSubcommand::Test(_) => dir.to_test_permissions_config()?,
|
|
_ => None,
|
|
};
|
|
match maybe_subcommand_permissions {
|
|
Some(permissions) => Some(permissions),
|
|
// do not error when the default set doesn't exist in order
|
|
// to allow providing `-P` unconditionally
|
|
None => dir.to_permissions_config()?.sets.get("default"),
|
|
}
|
|
} else {
|
|
Some(dir.to_permissions_config()?.get(name)?)
|
|
}
|
|
} else {
|
|
if !self.flags.has_permission() {
|
|
let set_config_permission_name = match &self.flags.subcommand {
|
|
DenoSubcommand::Bench(_) => dir
|
|
.to_bench_permissions_config()?
|
|
.map(|permissions| ("Bench", &permissions.base)),
|
|
DenoSubcommand::Compile(_) => dir
|
|
.to_compile_permissions_config()?
|
|
.map(|permissions| ("Compile", &permissions.base)),
|
|
DenoSubcommand::Test(_) => dir
|
|
.to_test_permissions_config()?
|
|
.map(|permissions| ("Test", &permissions.base)),
|
|
_ => None,
|
|
};
|
|
if let Some((name, config_file_url)) = set_config_permission_name {
|
|
// prevent people from wasting time wondering why benches/tests are failing
|
|
bail!(
|
|
"{} permissions were found in the config file. Did you mean to run with `-P` or a permission flag?\n at {}",
|
|
name,
|
|
config_file_url
|
|
);
|
|
}
|
|
}
|
|
|
|
None
|
|
};
|
|
Ok(config_permissions)
|
|
}
|
|
|
|
fn augment_import_permissions(&self, options: &mut PermissionsOptions) {
|
|
// do not add if the user specified --allow-all or --allow-import
|
|
if options.allow_import.is_none() {
|
|
options.allow_import = Some(self.implicit_allow_import());
|
|
}
|
|
}
|
|
|
|
fn implicit_allow_import(&self) -> Vec<String> {
|
|
// allow importing from anywhere when using cached only
|
|
if self.cache_setting() == CacheSetting::Only {
|
|
vec![] // allow all imports
|
|
} else {
|
|
// implicitly allow some trusted hosts and the CLI arg urls
|
|
let cli_arg_urls = self.get_cli_arg_urls();
|
|
let builtin_allowed_import_hosts = [
|
|
"jsr.io:443",
|
|
"deno.land:443",
|
|
"esm.sh:443",
|
|
"cdn.jsdelivr.net:443",
|
|
"raw.githubusercontent.com:443",
|
|
"gist.githubusercontent.com:443",
|
|
];
|
|
let mut imports = Vec::with_capacity(
|
|
builtin_allowed_import_hosts.len() + cli_arg_urls.len() + 1,
|
|
);
|
|
imports
|
|
.extend(builtin_allowed_import_hosts.iter().map(|s| s.to_string()));
|
|
// also add the JSR_URL env var
|
|
if let Some(jsr_host) = allow_import_host_from_url(jsr_url())
|
|
&& jsr_host != "jsr.io:443"
|
|
{
|
|
imports.push(jsr_host);
|
|
}
|
|
// include the cli arg urls
|
|
for url in cli_arg_urls {
|
|
if let Some(host) = allow_import_host_from_url(&url) {
|
|
imports.push(host);
|
|
}
|
|
}
|
|
imports
|
|
}
|
|
}
|
|
|
|
fn get_cli_arg_urls(&self) -> Vec<Cow<'_, Url>> {
|
|
fn files_to_urls(files: &[String]) -> Vec<Cow<'_, Url>> {
|
|
files.iter().filter_map(|f| file_to_url(f)).collect()
|
|
}
|
|
|
|
fn file_to_url(file: &str) -> Option<Cow<'_, Url>> {
|
|
Url::parse(file).ok().map(Cow::Owned)
|
|
}
|
|
|
|
self
|
|
.resolve_main_module()
|
|
.ok()
|
|
.map(|url| vec![Cow::Borrowed(url)])
|
|
.or_else(|| match &self.flags.subcommand {
|
|
DenoSubcommand::Cache(cache_flags) => {
|
|
Some(files_to_urls(&cache_flags.files))
|
|
}
|
|
DenoSubcommand::Check(check_flags) => {
|
|
Some(files_to_urls(&check_flags.files))
|
|
}
|
|
DenoSubcommand::Install(InstallFlags::Global(flags)) => {
|
|
file_to_url(&flags.module_url).map(|url| vec![url])
|
|
}
|
|
DenoSubcommand::Doc(DocFlags {
|
|
source_files: DocSourceFileFlag::Paths(paths),
|
|
..
|
|
}) => Some(files_to_urls(paths)),
|
|
DenoSubcommand::Info(InfoFlags {
|
|
file: Some(file), ..
|
|
}) => file_to_url(file).map(|url| vec![url]),
|
|
_ => None,
|
|
})
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
pub fn reload_flag(&self) -> bool {
|
|
self.flags.reload
|
|
}
|
|
|
|
pub fn seed(&self) -> Option<u64> {
|
|
self.flags.seed
|
|
}
|
|
|
|
pub fn sub_command(&self) -> &DenoSubcommand {
|
|
&self.flags.subcommand
|
|
}
|
|
|
|
pub fn trace_ops(&self) -> &Option<Vec<String>> {
|
|
&self.flags.trace_ops
|
|
}
|
|
|
|
pub fn take_binary_npm_command_name(&self) -> Option<String> {
|
|
match self.sub_command() {
|
|
DenoSubcommand::Run(flags) => {
|
|
const NPM_CMD_NAME_ENV_VAR_NAME: &str = "DENO_INTERNAL_NPM_CMD_NAME";
|
|
match std::env::var(NPM_CMD_NAME_ENV_VAR_NAME) {
|
|
Ok(var) => {
|
|
// remove the env var so that child sub processes won't pick this up
|
|
|
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
|
unsafe {
|
|
std::env::remove_var(NPM_CMD_NAME_ENV_VAR_NAME)
|
|
};
|
|
Some(var)
|
|
}
|
|
Err(_) => NpmPackageReqReference::from_str(&flags.script).ok().map(
|
|
|req_ref| npm_pkg_req_ref_to_binary_command(&req_ref).to_string(),
|
|
),
|
|
}
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn type_check_mode(&self) -> TypeCheckMode {
|
|
self.flags.type_check_mode
|
|
}
|
|
|
|
pub fn unsafely_ignore_certificate_errors(&self) -> &Option<Vec<String>> {
|
|
&self.flags.unsafely_ignore_certificate_errors
|
|
}
|
|
|
|
pub fn unstable_bare_node_builtins(&self) -> bool {
|
|
self.flags.unstable_config.bare_node_builtins
|
|
|| 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()
|
|
}
|
|
|
|
pub fn unstable_raw_imports(&self) -> bool {
|
|
self.flags.unstable_config.raw_imports
|
|
|| self.workspace().has_unstable("raw-imports")
|
|
}
|
|
|
|
pub fn unstable_lazy_dynamic_imports(&self) -> bool {
|
|
self.flags.unstable_config.lazy_dynamic_imports
|
|
|| self.workspace().has_unstable("lazy-dynamic-imports")
|
|
}
|
|
|
|
pub fn unstable_sloppy_imports(&self) -> bool {
|
|
self.flags.unstable_config.sloppy_imports
|
|
|| self.workspace().has_unstable("sloppy-imports")
|
|
}
|
|
|
|
pub fn unstable_features(&self) -> Vec<&str> {
|
|
let from_config_file = self.workspace().unstable_features();
|
|
let unstable_features = from_config_file
|
|
.iter()
|
|
.map(|s| s.as_str())
|
|
.chain(
|
|
self
|
|
.flags
|
|
.unstable_config
|
|
.features
|
|
.iter()
|
|
.filter(|f| !from_config_file.contains(f))
|
|
.map(|s| s.as_str()),
|
|
)
|
|
.collect::<Vec<_>>();
|
|
|
|
if !unstable_features.is_empty() {
|
|
let all_valid_unstable_flags: Vec<&str> = deno_runtime::UNSTABLE_FEATURES
|
|
.iter()
|
|
.map(|feature| feature.name)
|
|
.chain(["fmt-component", "fmt-sql", "npm-lazy-caching"])
|
|
.collect();
|
|
|
|
// check and warn if the unstable flag of config file isn't supported, by
|
|
// iterating through the vector holding the unstable flags
|
|
for unstable_value_from_config_file in &unstable_features {
|
|
if !all_valid_unstable_flags.contains(unstable_value_from_config_file) {
|
|
log::warn!(
|
|
"{} '{}' isn't a valid unstable feature",
|
|
colors::yellow("Warning"),
|
|
unstable_value_from_config_file
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
unstable_features
|
|
}
|
|
|
|
pub fn v8_flags(&self) -> &Vec<String> {
|
|
&self.flags.v8_flags
|
|
}
|
|
|
|
pub fn code_cache_enabled(&self) -> bool {
|
|
self.flags.code_cache_enabled
|
|
}
|
|
|
|
pub fn watch_paths(&self) -> Vec<PathBuf> {
|
|
let mut full_paths = Vec::new();
|
|
if let DenoSubcommand::Run(RunFlags {
|
|
watch: Some(WatchFlagsWithPaths { paths, .. }),
|
|
..
|
|
})
|
|
| DenoSubcommand::Serve(ServeFlags {
|
|
watch: Some(WatchFlagsWithPaths { paths, .. }),
|
|
..
|
|
}) = &self.flags.subcommand
|
|
{
|
|
full_paths.extend(paths.iter().map(|path| self.initial_cwd.join(path)));
|
|
}
|
|
|
|
if let Some(env_file_names) = &self.flags.env_file {
|
|
// Only watch the exact environment files specified
|
|
full_paths.extend(
|
|
env_file_names
|
|
.iter()
|
|
.map(|name| self.initial_cwd.join(name)),
|
|
);
|
|
}
|
|
|
|
if let Ok(Some(import_map_path)) = self
|
|
.resolve_specified_import_map_specifier()
|
|
.map(|ms| ms.and_then(|ref s| s.to_file_path().ok()))
|
|
{
|
|
full_paths.push(import_map_path);
|
|
}
|
|
|
|
for (_, folder) in self.workspace().config_folders() {
|
|
if let Some(deno_json) = &folder.deno_json
|
|
&& deno_json.specifier.scheme() == "file"
|
|
&& let Ok(path) = deno_json.specifier.to_file_path()
|
|
{
|
|
full_paths.push(path);
|
|
}
|
|
if let Some(pkg_json) = &folder.pkg_json {
|
|
full_paths.push(pkg_json.path.clone());
|
|
}
|
|
}
|
|
|
|
full_paths
|
|
}
|
|
|
|
pub fn lifecycle_scripts_config(&self) -> LifecycleScriptsConfig {
|
|
LifecycleScriptsConfig {
|
|
allowed: self.flags.allow_scripts.clone(),
|
|
initial_cwd: self.initial_cwd.clone(),
|
|
root_dir: self.workspace().root_dir_path(),
|
|
explicit_install: matches!(
|
|
self.sub_command(),
|
|
DenoSubcommand::Install(_)
|
|
| DenoSubcommand::Cache(_)
|
|
| DenoSubcommand::Add(_)
|
|
),
|
|
}
|
|
}
|
|
|
|
pub fn unstable_npm_lazy_caching(&self) -> bool {
|
|
self.flags.unstable_config.npm_lazy_caching
|
|
|| self.workspace().has_unstable("npm-lazy-caching")
|
|
}
|
|
|
|
pub fn default_npm_caching_strategy(&self) -> NpmCachingStrategy {
|
|
if matches!(
|
|
self.sub_command(),
|
|
DenoSubcommand::Install(InstallFlags::Local(
|
|
InstallFlagsLocal::TopLevel | InstallFlagsLocal::Add(_)
|
|
)) | DenoSubcommand::Add(_)
|
|
| DenoSubcommand::Outdated(_)
|
|
) {
|
|
NpmCachingStrategy::Manual
|
|
} else if self.flags.unstable_config.npm_lazy_caching {
|
|
NpmCachingStrategy::Lazy
|
|
} else {
|
|
NpmCachingStrategy::Eager
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Error)]
|
|
#[error("Bad URL for import map.")]
|
|
pub struct ImportMapSpecifierResolveError {
|
|
#[source]
|
|
source: deno_path_util::ResolveUrlOrPathError,
|
|
}
|
|
|
|
fn resolve_import_map_specifier(
|
|
maybe_import_map_path: Option<&str>,
|
|
maybe_config_file: Option<&ConfigFile>,
|
|
current_dir: &Path,
|
|
) -> Result<Option<Url>, ImportMapSpecifierResolveError> {
|
|
if let Some(import_map_path) = maybe_import_map_path {
|
|
if let Some(config_file) = &maybe_config_file
|
|
&& config_file.json.import_map.is_some()
|
|
{
|
|
log::warn!(
|
|
"{} the configuration file \"{}\" contains an entry for \"importMap\" that is being ignored.",
|
|
colors::yellow("Warning"),
|
|
config_file.specifier,
|
|
);
|
|
}
|
|
let specifier =
|
|
deno_path_util::resolve_url_or_path(import_map_path, current_dir)
|
|
.map_err(|source| ImportMapSpecifierResolveError { source })?;
|
|
Ok(Some(specifier))
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
/// Resolves the no_prompt value based on the cli flags and environment.
|
|
pub fn resolve_no_prompt(flags: &PermissionFlags) -> bool {
|
|
flags.no_prompt || has_flag_env_var("DENO_NO_PROMPT")
|
|
}
|
|
|
|
pub fn config_to_deno_graph_workspace_member(
|
|
config: &ConfigFile,
|
|
) -> Result<deno_graph::WorkspaceMember, AnyError> {
|
|
let name: StackString = match &config.json.name {
|
|
Some(name) => name.as_str().into(),
|
|
None => bail!("Missing 'name' field in config file."),
|
|
};
|
|
let version = match &config.json.version {
|
|
Some(name) => Some(deno_semver::Version::parse_standard(name)?),
|
|
None => None,
|
|
};
|
|
Ok(deno_graph::WorkspaceMember {
|
|
base: config.specifier.join("./").unwrap(),
|
|
name,
|
|
version,
|
|
exports: config.to_exports_config()?.into_map(),
|
|
})
|
|
}
|
|
|
|
pub fn get_default_v8_flags() -> Vec<String> {
|
|
vec![
|
|
"--stack-size=1024".to_string(),
|
|
"--js-explicit-resource-management".to_string(),
|
|
// TODO(bartlomieju): I think this can be removed as it's handled by `deno_core`
|
|
// and its settings.
|
|
// deno_ast removes TypeScript `assert` keywords, so this flag only affects JavaScript
|
|
// TODO(petamoriken): Need to check TypeScript `assert` keywords in deno_ast
|
|
"--no-harmony-import-assertions".to_string(),
|
|
]
|
|
}
|
|
|
|
pub fn parallelism_count(parallel: bool) -> NonZeroUsize {
|
|
parallel
|
|
.then(|| {
|
|
if let Ok(value) = env::var("DENO_JOBS") {
|
|
value.parse::<NonZeroUsize>().ok()
|
|
} else {
|
|
std::thread::available_parallelism().ok()
|
|
}
|
|
})
|
|
.flatten()
|
|
.unwrap_or_else(|| NonZeroUsize::new(1).unwrap())
|
|
}
|
|
|
|
/// Gets the --allow-import host from the provided url
|
|
fn allow_import_host_from_url(url: &Url) -> Option<String> {
|
|
let host = url.host()?;
|
|
if let Some(port) = url.port() {
|
|
Some(format!("{}:{}", host, port))
|
|
} else {
|
|
match url.scheme() {
|
|
"https" => Some(format!("{}:443", host)),
|
|
"http" => Some(format!("{}:80", host)),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
// DO NOT make this public. People should use `cli_options.permissions_options/permissions_options_for_dir`
|
|
fn flags_to_permissions_options(
|
|
flags: &PermissionFlags,
|
|
mut config: Option<&PermissionsObjectWithBase>,
|
|
) -> Result<PermissionsOptions, AnyError> {
|
|
fn handle_allow(
|
|
allow_all_flag: bool,
|
|
allow_all_config: Option<bool>,
|
|
value: Option<&Vec<String>>,
|
|
config: Option<&PermissionConfigValue>,
|
|
parse_config_value: &impl Fn(&str) -> String,
|
|
) -> Option<Vec<String>> {
|
|
if allow_all_flag {
|
|
Some(vec![])
|
|
} else if let Some(value) = value {
|
|
Some(value.clone())
|
|
} else if let Some(config) = config {
|
|
match config {
|
|
PermissionConfigValue::All => Some(vec![]),
|
|
PermissionConfigValue::Some(items) => {
|
|
if items.is_empty() {
|
|
None
|
|
} else {
|
|
Some(
|
|
items
|
|
.iter()
|
|
.map(|value| parse_config_value(value))
|
|
.collect(),
|
|
)
|
|
}
|
|
}
|
|
PermissionConfigValue::None => None,
|
|
}
|
|
} else if allow_all_config == Some(true) {
|
|
Some(vec![])
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn handle_deny(
|
|
value: Option<&Vec<String>>,
|
|
config: Option<&PermissionConfigValue>,
|
|
parse_config_value: &impl Fn(&str) -> String,
|
|
) -> Option<Vec<String>> {
|
|
if let Some(value) = value {
|
|
Some(value.clone())
|
|
} else if let Some(config) = config {
|
|
match config {
|
|
PermissionConfigValue::All => Some(vec![]),
|
|
PermissionConfigValue::Some(items) => Some(
|
|
items
|
|
.iter()
|
|
.map(|value| parse_config_value(value))
|
|
.collect(),
|
|
),
|
|
PermissionConfigValue::None => None,
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
if flags.allow_all {
|
|
config = None;
|
|
}
|
|
|
|
let config_dir = match &config {
|
|
Some(config) => {
|
|
let mut path = deno_path_util::url_to_file_path(&config.base)?;
|
|
path.pop();
|
|
Some(path)
|
|
}
|
|
None => None,
|
|
};
|
|
|
|
let make_fs_config_value_absolute = |value: &str| match &config_dir {
|
|
Some(dir_path) => {
|
|
PathDescriptor::new_known_cwd(Cow::Borrowed(Path::new(value)), dir_path)
|
|
.into_path_buf()
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
}
|
|
None => value.to_string(),
|
|
};
|
|
let make_run_config_value_absolute = |value: &str| match &config_dir {
|
|
Some(dir_path) => {
|
|
if AllowRunDescriptor::is_path(value) {
|
|
PathDescriptor::new_known_cwd(Cow::Borrowed(Path::new(value)), dir_path)
|
|
.into_path_buf()
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
} else {
|
|
value.to_string()
|
|
}
|
|
}
|
|
None => value.to_string(),
|
|
};
|
|
let no_op = |value: &str| value.to_string();
|
|
|
|
Ok(PermissionsOptions {
|
|
allow_env: handle_allow(
|
|
flags.allow_all,
|
|
config.and_then(|c| c.permissions.all),
|
|
flags.allow_env.as_ref(),
|
|
config.and_then(|c| c.permissions.env.allow.as_ref()),
|
|
&no_op,
|
|
),
|
|
deny_env: handle_deny(
|
|
flags.deny_env.as_ref(),
|
|
config.and_then(|c| c.permissions.env.deny.as_ref()),
|
|
&no_op,
|
|
),
|
|
allow_net: handle_allow(
|
|
flags.allow_all,
|
|
config.and_then(|c| c.permissions.all),
|
|
flags.allow_net.as_ref(),
|
|
config.and_then(|c| c.permissions.net.allow.as_ref()),
|
|
&no_op,
|
|
),
|
|
deny_net: handle_deny(
|
|
flags.deny_net.as_ref(),
|
|
config.and_then(|c| c.permissions.net.deny.as_ref()),
|
|
&no_op,
|
|
),
|
|
allow_ffi: handle_allow(
|
|
flags.allow_all,
|
|
config.and_then(|c| c.permissions.all),
|
|
flags.allow_ffi.as_ref(),
|
|
config.and_then(|c| c.permissions.ffi.allow.as_ref()),
|
|
&make_fs_config_value_absolute,
|
|
),
|
|
deny_ffi: handle_deny(
|
|
flags.deny_ffi.as_ref(),
|
|
config.and_then(|c| c.permissions.ffi.deny.as_ref()),
|
|
&make_fs_config_value_absolute,
|
|
),
|
|
allow_read: handle_allow(
|
|
flags.allow_all,
|
|
config.and_then(|c| c.permissions.all),
|
|
flags.allow_read.as_ref(),
|
|
config.and_then(|c| c.permissions.read.allow.as_ref()),
|
|
&make_fs_config_value_absolute,
|
|
),
|
|
deny_read: handle_deny(
|
|
flags.deny_read.as_ref(),
|
|
config.and_then(|c| c.permissions.read.deny.as_ref()),
|
|
&make_fs_config_value_absolute,
|
|
),
|
|
allow_run: handle_allow(
|
|
flags.allow_all,
|
|
config.and_then(|c| c.permissions.all),
|
|
flags.allow_run.as_ref(),
|
|
config.and_then(|c| c.permissions.run.allow.as_ref()),
|
|
&make_run_config_value_absolute,
|
|
),
|
|
deny_run: handle_deny(
|
|
flags.deny_run.as_ref(),
|
|
config.and_then(|c| c.permissions.run.deny.as_ref()),
|
|
&make_run_config_value_absolute,
|
|
),
|
|
allow_sys: handle_allow(
|
|
flags.allow_all,
|
|
config.and_then(|c| c.permissions.all),
|
|
flags.allow_sys.as_ref(),
|
|
config.and_then(|c| c.permissions.sys.allow.as_ref()),
|
|
&no_op,
|
|
),
|
|
deny_sys: handle_deny(
|
|
flags.deny_sys.as_ref(),
|
|
config.and_then(|c| c.permissions.sys.deny.as_ref()),
|
|
&no_op,
|
|
),
|
|
allow_write: handle_allow(
|
|
flags.allow_all,
|
|
config.and_then(|c| c.permissions.all),
|
|
flags.allow_write.as_ref(),
|
|
config.and_then(|c| c.permissions.write.allow.as_ref()),
|
|
&make_fs_config_value_absolute,
|
|
),
|
|
deny_write: handle_deny(
|
|
flags.deny_write.as_ref(),
|
|
config.and_then(|c| c.permissions.write.deny.as_ref()),
|
|
&make_fs_config_value_absolute,
|
|
),
|
|
allow_import: handle_allow(
|
|
flags.allow_all,
|
|
config.and_then(|c| c.permissions.all),
|
|
flags.allow_import.as_ref(),
|
|
config.and_then(|c| c.permissions.import.allow.as_ref()),
|
|
&no_op,
|
|
),
|
|
deny_import: handle_deny(
|
|
flags.deny_import.as_ref(),
|
|
config.and_then(|c| c.permissions.import.deny.as_ref()),
|
|
&no_op,
|
|
),
|
|
prompt: !resolve_no_prompt(flags),
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use deno_config::deno_json::AllowDenyPermissionConfig;
|
|
use deno_config::deno_json::PermissionsObject;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn resolve_import_map_flags_take_precedence() {
|
|
let config_text = r#"{
|
|
"importMap": "import_map.json"
|
|
}"#;
|
|
let cwd = &std::env::current_dir().unwrap();
|
|
let config_specifier = Url::parse("file:///deno/deno.jsonc").unwrap();
|
|
let config_file = ConfigFile::new(config_text, config_specifier).unwrap();
|
|
let actual = resolve_import_map_specifier(
|
|
Some("import-map.json"),
|
|
Some(&config_file),
|
|
cwd,
|
|
);
|
|
let import_map_path = cwd.join("import-map.json");
|
|
let expected_specifier =
|
|
deno_path_util::url_from_file_path(&import_map_path).unwrap();
|
|
assert!(actual.is_ok());
|
|
let actual = actual.unwrap();
|
|
assert_eq!(actual, Some(expected_specifier));
|
|
}
|
|
|
|
#[test]
|
|
fn resolve_import_map_none() {
|
|
let config_text = r#"{}"#;
|
|
let config_specifier = Url::parse("file:///deno/deno.jsonc").unwrap();
|
|
let config_file = ConfigFile::new(config_text, config_specifier).unwrap();
|
|
let actual =
|
|
resolve_import_map_specifier(None, Some(&config_file), Path::new("/"));
|
|
assert!(actual.is_ok());
|
|
let actual = actual.unwrap();
|
|
assert_eq!(actual, None);
|
|
}
|
|
|
|
#[test]
|
|
fn resolve_import_map_no_config() {
|
|
let actual = resolve_import_map_specifier(None, None, Path::new("/"));
|
|
assert!(actual.is_ok());
|
|
let actual = actual.unwrap();
|
|
assert_eq!(actual, None);
|
|
}
|
|
|
|
#[test]
|
|
fn jsr_urls() {
|
|
let reg_url = jsr_url();
|
|
assert!(reg_url.as_str().ends_with('/'));
|
|
let reg_api_url = jsr_api_url();
|
|
assert!(reg_api_url.as_str().ends_with('/'));
|
|
}
|
|
|
|
#[test]
|
|
fn test_allow_import_host_from_url() {
|
|
fn parse(text: &str) -> Option<String> {
|
|
allow_import_host_from_url(&Url::parse(text).unwrap())
|
|
}
|
|
|
|
assert_eq!(
|
|
parse("http://127.0.0.1:4250"),
|
|
Some("127.0.0.1:4250".to_string())
|
|
);
|
|
assert_eq!(parse("http://jsr.io"), Some("jsr.io:80".to_string()));
|
|
assert_eq!(
|
|
parse("https://example.com"),
|
|
Some("example.com:443".to_string())
|
|
);
|
|
assert_eq!(
|
|
parse("http://example.com"),
|
|
Some("example.com:80".to_string())
|
|
);
|
|
assert_eq!(parse("file:///example.com"), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_flags_to_permission_options() {
|
|
let base_dir = std::env::current_dir().unwrap().join("sub");
|
|
{
|
|
let flags = PermissionFlags::default();
|
|
let config = PermissionsObjectWithBase {
|
|
base: deno_path_util::url_from_file_path(&base_dir.join("deno.json"))
|
|
.unwrap(),
|
|
permissions: PermissionsObject {
|
|
all: None,
|
|
read: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec![
|
|
".".to_string(),
|
|
"./read-allow".to_string(),
|
|
])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"./read-deny".to_string(),
|
|
])),
|
|
},
|
|
write: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec![
|
|
"./write-allow".to_string(),
|
|
])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"./write-deny".to_string(),
|
|
])),
|
|
},
|
|
import: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec![
|
|
"jsr.io".to_string(),
|
|
])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"example.com".to_string(),
|
|
])),
|
|
},
|
|
env: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec![
|
|
"env-allow".to_string(),
|
|
])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"env-deny".to_string(),
|
|
])),
|
|
},
|
|
net: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec![
|
|
"net-allow".to_string(),
|
|
])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"net-deny".to_string(),
|
|
])),
|
|
},
|
|
run: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec![
|
|
"run-allow".to_string(),
|
|
"./relative-run-allow".to_string(),
|
|
])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"run-deny".to_string(),
|
|
"./relative-run-deny".to_string(),
|
|
])),
|
|
},
|
|
ffi: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec![
|
|
"./ffi-allow".to_string(),
|
|
])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"./ffi-deny".to_string(),
|
|
])),
|
|
},
|
|
sys: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec![
|
|
"sys-allow".to_string(),
|
|
])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"sys-deny".to_string(),
|
|
])),
|
|
},
|
|
},
|
|
};
|
|
let permissions_options =
|
|
flags_to_permissions_options(&flags, Some(&config)).unwrap();
|
|
assert_eq!(
|
|
permissions_options,
|
|
PermissionsOptions {
|
|
allow_env: Some(vec!["env-allow".to_string()]),
|
|
deny_env: Some(vec!["env-deny".to_string()]),
|
|
allow_net: Some(vec!["net-allow".to_string()]),
|
|
deny_net: Some(vec!["net-deny".to_string()]),
|
|
allow_ffi: Some(vec![
|
|
base_dir
|
|
.join("ffi-allow")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
deny_ffi: Some(vec![
|
|
base_dir
|
|
.join("ffi-deny")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
allow_read: Some(vec![
|
|
base_dir.clone().into_os_string().into_string().unwrap(),
|
|
base_dir
|
|
.join("read-allow")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
deny_read: Some(vec![
|
|
base_dir
|
|
.join("read-deny")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
allow_run: Some(vec![
|
|
"run-allow".to_string(),
|
|
base_dir
|
|
.join("relative-run-allow")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
deny_run: Some(vec![
|
|
"run-deny".to_string(),
|
|
base_dir
|
|
.join("relative-run-deny")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
allow_sys: Some(vec!["sys-allow".to_string()]),
|
|
deny_sys: Some(vec!["sys-deny".to_string()]),
|
|
allow_write: Some(vec![
|
|
base_dir
|
|
.join("write-allow")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
deny_write: Some(vec![
|
|
base_dir
|
|
.join("write-deny")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
allow_import: Some(vec!["jsr.io".to_string()]),
|
|
deny_import: Some(vec!["example.com".to_string()]),
|
|
prompt: true
|
|
}
|
|
);
|
|
}
|
|
{
|
|
let flags = PermissionFlags {
|
|
allow_read: Some(vec!["./folder".to_string()]),
|
|
..Default::default()
|
|
};
|
|
let config = PermissionsObjectWithBase {
|
|
base: deno_path_util::url_from_file_path(&base_dir.join("deno.json"))
|
|
.unwrap(),
|
|
permissions: PermissionsObject {
|
|
// will use all permissions except for the explicitly specified permissions
|
|
// and the explicit flag will replace
|
|
all: Some(true),
|
|
write: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec![
|
|
"./write-allow".to_string(),
|
|
])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"./write-deny".to_string(),
|
|
])),
|
|
},
|
|
..Default::default()
|
|
},
|
|
};
|
|
let permissions_options =
|
|
flags_to_permissions_options(&flags, Some(&config)).unwrap();
|
|
assert_eq!(
|
|
permissions_options,
|
|
PermissionsOptions {
|
|
allow_env: Some(vec![]),
|
|
deny_env: None,
|
|
allow_net: Some(vec![]),
|
|
deny_net: None,
|
|
allow_ffi: Some(vec![]),
|
|
deny_ffi: None,
|
|
allow_read: Some(vec!["./folder".to_string()]),
|
|
deny_read: None,
|
|
allow_run: Some(vec![]),
|
|
deny_run: None,
|
|
allow_sys: Some(vec![]),
|
|
deny_sys: None,
|
|
allow_write: Some(vec![
|
|
base_dir
|
|
.join("write-allow")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
deny_write: Some(vec![
|
|
base_dir
|
|
.join("write-deny")
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap()
|
|
]),
|
|
allow_import: Some(vec![]),
|
|
deny_import: None,
|
|
prompt: true
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|