Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 116 additions & 1 deletion cli/args/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,16 @@ pub enum InstallFlagsLocal {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InstallTopLevelFlags {
pub lockfile_only: bool,
pub production: bool,
pub skip_types: bool,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InstallEntrypointsFlags {
pub entrypoints: Vec<String>,
pub lockfile_only: bool,
pub production: bool,
pub skip_types: bool,
}

#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -3586,6 +3590,23 @@ These must be added to the path manually if required."), UnstableArgsConfig::Res
.conflicts_with("global"),
)
.arg(lockfile_only_arg().conflicts_with("global"))
.arg(
Arg::new("prod")
.long("prod")
.alias("production")
.help("Only install production dependencies (excludes devDependencies)")
.action(ArgAction::SetTrue)
.conflicts_with("global")
.conflicts_with("dev"),
)
.arg(
Arg::new("skip-types")
.long("skip-types")
.help(cstr!("Exclude @types/* packages from installation.
<y>Be careful, as it uses a name-based heuristic and may skip packages that ship runtime code.</>"))
.action(ArgAction::SetTrue)
.requires("prod"),
)
})
}

Expand Down Expand Up @@ -6753,12 +6774,16 @@ fn install_parse(
return Ok(());
}
let lockfile_only = matches.get_flag("lockfile-only");
let production = matches.get_flag("prod");
let skip_types = matches.get_flag("skip-types");
if matches.get_flag("entrypoint") {
let entrypoints = matches.remove_many::<String>("cmd").unwrap_or_default();
flags.subcommand = DenoSubcommand::Install(InstallFlags::Local(
InstallFlagsLocal::Entrypoints(InstallEntrypointsFlags {
entrypoints: entrypoints.collect(),
lockfile_only,
production,
skip_types,
}),
));
} else if let Some(add_files) = matches
Expand All @@ -6777,7 +6802,11 @@ fn install_parse(
))
} else {
flags.subcommand = DenoSubcommand::Install(InstallFlags::Local(
InstallFlagsLocal::TopLevel(InstallTopLevelFlags { lockfile_only }),
InstallFlagsLocal::TopLevel(InstallTopLevelFlags {
lockfile_only,
production,
skip_types,
}),
));
}
Ok(())
Expand Down Expand Up @@ -14291,6 +14320,92 @@ mod tests {
);
}

#[test]
fn install_production() {
let r = flags_from_vec(svec!["deno", "install", "--prod"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Install(InstallFlags::Local(
InstallFlagsLocal::TopLevel(InstallTopLevelFlags {
lockfile_only: false,
production: true,
skip_types: false,
})
)),
..Flags::default()
}
);
}

#[test]
fn install_production_with_entrypoint() {
let r = flags_from_vec(svec![
"deno",
"install",
"--prod",
"--entrypoint",
"main.ts"
]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Install(InstallFlags::Local(
InstallFlagsLocal::Entrypoints(InstallEntrypointsFlags {
entrypoints: svec!["main.ts"],
lockfile_only: false,
production: true,
skip_types: false,
})
)),
..Flags::default()
}
);
}

#[test]
fn install_production_with_skip_types() {
let r = flags_from_vec(svec!["deno", "install", "--prod", "--skip-types"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Install(InstallFlags::Local(
InstallFlagsLocal::TopLevel(InstallTopLevelFlags {
lockfile_only: false,
production: true,
skip_types: true,
})
)),
..Flags::default()
}
);
}

#[test]
fn install_skip_types_requires_prod() {
let r = flags_from_vec(svec!["deno", "install", "--skip-types"]);
assert!(r.is_err());
}

#[test]
fn install_production_conflicts_with_global() {
let r = flags_from_vec(svec![
"deno",
"install",
"--prod",
"--global",
"jsr:@std/http/file-server"
]);
assert!(r.is_err());
}

#[test]
fn install_production_conflicts_with_dev() {
let r =
flags_from_vec(svec!["deno", "install", "--prod", "--dev", "npm:chalk"]);
assert!(r.is_err());
}

#[test]
fn jupyter_unstable_flags() {
let r = flags_from_vec(svec![
Expand Down
3 changes: 3 additions & 0 deletions cli/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,9 @@ impl CliOptions {
DenoSubcommand::Add(_) => GraphKind::All,
DenoSubcommand::Cache(_) => GraphKind::All,
DenoSubcommand::Check(_) => GraphKind::TypesOnly,
DenoSubcommand::Install(InstallFlags::Local(
InstallFlagsLocal::Entrypoints(flags),
)) if flags.production => GraphKind::CodeOnly,
DenoSubcommand::Install(InstallFlags::Local(_)) => GraphKind::All,
_ => self.type_check_mode().as_graph_kind(),
}
Expand Down
20 changes: 20 additions & 0 deletions cli/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,26 @@ impl CliFactory {
),
caching_strategy: cli_options.default_npm_caching_strategy(),
lifecycle_scripts_config: cli_options.lifecycle_scripts_config(),
production: match cli_options.sub_command() {
DenoSubcommand::Install(InstallFlags::Local(flags)) => {
match flags {
InstallFlagsLocal::TopLevel(f) => f.production,
InstallFlagsLocal::Entrypoints(f) => f.production,
InstallFlagsLocal::Add(_) => false,
}
}
_ => false,
},
skip_types: match cli_options.sub_command() {
DenoSubcommand::Install(InstallFlags::Local(flags)) => {
match flags {
InstallFlagsLocal::TopLevel(f) => f.skip_types,
InstallFlagsLocal::Entrypoints(f) => f.skip_types,
InstallFlagsLocal::Add(_) => false,
}
}
_ => false,
},
resolve_npm_resolution_snapshot: Box::new(|| {
deno_lib::args::resolve_npm_resolution_snapshot(&CliSys::default())
}),
Expand Down
2 changes: 2 additions & 0 deletions cli/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ async fn run_subcommand(
self::args::InstallEntrypointsFlags {
entrypoints: cache_flags.files,
lockfile_only: false,
production: false,
skip_types: false,
},
)
.await
Expand Down
2 changes: 2 additions & 0 deletions cli/lsp/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,8 @@ impl ConfigData {
cache_setting: NpmCacheSetting::Use,
caching_strategy: NpmCachingStrategy::Eager,
lifecycle_scripts_config: LifecycleScriptsConfig::default(),
production: false,
skip_types: false,
resolve_npm_resolution_snapshot: Box::new(|| Ok(None)),
},
);
Expand Down
2 changes: 2 additions & 0 deletions cli/tools/installer/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,8 @@ async fn setup_config_dir(
let entrypoint_flags = InstallEntrypointsFlags {
lockfile_only: false,
entrypoints: vec![bin_name_and_url.module_url.to_string()],
production: false,
skip_types: false,
};
new_flags.subcommand = DenoSubcommand::Install(InstallFlags::Local(
InstallFlagsLocal::Entrypoints(entrypoint_flags.clone()),
Expand Down
6 changes: 6 additions & 0 deletions libs/npm_installer/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ pub struct NpmInstallerFactoryOptions {
pub caching_strategy: NpmCachingStrategy,
pub clean_on_install: bool,
pub lifecycle_scripts_config: LifecycleScriptsConfig,
/// Only install production dependencies (excludes devDependencies).
pub production: bool,
/// Exclude @types/* packages from installation.
pub skip_types: bool,
/// Resolves the npm resolution snapshot from the environment.
pub resolve_npm_resolution_snapshot: ResolveNpmResolutionSnapshotFn,
}
Expand Down Expand Up @@ -362,6 +366,8 @@ impl<
npm_cache.clone(),
Arc::new(NpmInstallDepsProvider::from_workspace(
&workspace_factory.workspace_directory()?.workspace,
self.options.production,
self.options.skip_types,
)),
registry_info_provider.clone(),
self.resolver_factory.npm_resolution().clone(),
Expand Down
24 changes: 20 additions & 4 deletions libs/npm_installer/package_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ impl NpmInstallDepsProvider {
Self::default()
}

pub fn from_workspace(workspace: &Arc<Workspace>) -> Self {
pub fn from_workspace(
workspace: &Arc<Workspace>,
production: bool,
skip_types: bool,
) -> Self {
// todo(dsherret): estimate capacity?
let mut local_pkgs = Vec::new();
let mut remote_pkgs = Vec::new();
Expand All @@ -83,6 +87,11 @@ impl NpmInstallDepsProvider {
continue;
};
let pkg_req = npm_req_ref.into_inner().req;

if skip_types && pkg_req.name.starts_with("@types/") {
continue;
}

let workspace_pkg = workspace_npm_pkgs
.iter()
.find(|pkg| pkg.matches_req(&pkg_req));
Expand Down Expand Up @@ -112,9 +121,13 @@ impl NpmInstallDepsProvider {
let mut pkg_pkgs = Vec::with_capacity(
deps.dependencies.len() + deps.dev_dependencies.len(),
);
for (alias, dep) in
deps.dependencies.iter().chain(deps.dev_dependencies.iter())
{
let empty = Default::default();
let dev_deps = if production {
&empty
} else {
&deps.dev_dependencies
};
for (alias, dep) in deps.dependencies.iter().chain(dev_deps.iter()) {
let dep = match dep {
Ok(dep) => dep,
Err(err) => {
Expand All @@ -136,6 +149,9 @@ impl NpmInstallDepsProvider {
})
}
PackageJsonDepValue::Req(pkg_req) => {
if skip_types && pkg_req.name.starts_with("@types/") {
continue;
}
let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| {
pkg.matches_req(pkg_req)
// do not resolve to the current package
Expand Down
Loading
Loading