diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 800d6ff5a0db50..af57e64e8e6805 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -585,7 +585,7 @@ pub struct Flags { pub argv: Vec, pub subcommand: DenoSubcommand, - pub frozen_lockfile: bool, + pub frozen_lockfile: Option, pub ca_stores: Option>, pub ca_data: Option, pub cache_blocklist: Vec, @@ -4924,7 +4924,7 @@ fn cached_only_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { fn frozen_lockfile_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { if let Some(&v) = matches.get_one::("frozen") { - flags.frozen_lockfile = v; + flags.frozen_lockfile = Some(v); } } @@ -10384,10 +10384,10 @@ mod tests { #[test] fn run_with_frozen_lockfile() { let cases = [ - (Some("--frozen"), true), - (Some("--frozen=true"), true), - (Some("--frozen=false"), false), - (None, false), + (Some("--frozen"), Some(true)), + (Some("--frozen=true"), Some(true)), + (Some("--frozen=false"), Some(false)), + (None, None), ]; for (flag, frozen) in cases { let mut args = svec!["deno", "run"]; diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index c9afecd98363f7..d1a788f324c69f 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -147,22 +147,28 @@ impl CliLockfile { }, }; + let root_folder = workspace.root_folder_configs(); + // CLI flag takes precedence over the config + let frozen = flags.frozen_lockfile.unwrap_or_else(|| { + root_folder + .deno_json + .as_ref() + .and_then(|c| c.to_lock_config().ok().flatten().map(|c| c.frozen())) + .unwrap_or(false) + }); + let lockfile = if flags.lock_write { log::warn!( "{} \"--lock-write\" flag is deprecated and will be removed in Deno 2.", crate::colors::yellow("Warning") ); - CliLockfile::new( - Lockfile::new_empty(filename, true), - flags.frozen_lockfile, - ) + CliLockfile::new(Lockfile::new_empty(filename, true), frozen) } else { - Self::read_from_path(filename, flags.frozen_lockfile)? + Self::read_from_path(filename, frozen)? }; // initialize the lockfile with the workspace's configuration let root_url = workspace.root_dir(); - let root_folder = workspace.root_folder_configs(); let config = deno_lockfile::WorkspaceConfig { root: WorkspaceMemberConfig { package_json_deps: pkg_json_deps(root_folder.pkg_json.as_deref()), diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 1d6601933bbe09..e3e909a647293e 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1846,7 +1846,12 @@ fn resolve_lockfile_from_workspace( return None; } }; - resolve_lockfile_from_path(lockfile_path) + let frozen = workspace + .workspace + .root_deno_json() + .and_then(|c| c.to_lock_config().ok().flatten().map(|c| c.frozen())) + .unwrap_or(false); + resolve_lockfile_from_path(lockfile_path, frozen) } fn resolve_node_modules_dir( @@ -1875,8 +1880,11 @@ fn resolve_node_modules_dir( canonicalize_path_maybe_not_exists(&node_modules_dir).ok() } -fn resolve_lockfile_from_path(lockfile_path: PathBuf) -> Option { - match CliLockfile::read_from_path(lockfile_path, false) { +fn resolve_lockfile_from_path( + lockfile_path: PathBuf, + frozen: bool, +) -> Option { + match CliLockfile::read_from_path(lockfile_path, frozen) { Ok(value) => { if value.filename.exists() { if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename) diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json index 96a95ca1626320..1687acdd2aa707 100644 --- a/cli/schemas/config-file.v1.json +++ b/cli/schemas/config-file.v1.json @@ -548,8 +548,20 @@ }, "lock": { "description": "Whether to use a lock file or the path to use for the lock file. Can be overridden by CLI arguments.", - "type": ["string", "boolean"], - "default": true + "type": ["string", "boolean", "object"], + "default": true, + "properties": { + "path": { + "type": "string", + "description": "The path to use for the lock file.", + "default": "deno.lock" + }, + "frozen": { + "type": "boolean", + "description": "Whether to exit with an error if lock file is out of date.", + "default": false + } + } }, "unstable": { "type": "array", diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 648b766b855e0e..456e5c1a68a98b 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -472,7 +472,7 @@ async fn resolve_shim_data( executable_args.push("--cached-only".to_string()); } - if flags.frozen_lockfile { + if flags.frozen_lockfile.unwrap_or(false) { executable_args.push("--frozen".to_string()); } diff --git a/tests/specs/lockfile/frozen_lockfile/__test__.jsonc b/tests/specs/lockfile/frozen_lockfile/__test__.jsonc index 76712a9132031a..b8ea2dbd9bc3a2 100644 --- a/tests/specs/lockfile/frozen_lockfile/__test__.jsonc +++ b/tests/specs/lockfile/frozen_lockfile/__test__.jsonc @@ -114,6 +114,35 @@ } ] }, + + "lockfile_config": { + "steps": [ + { + "args": [ + "eval", + "Deno.writeTextFileSync('deno.json', JSON.stringify({ lock: { frozen: true }, ...JSON.parse(Deno.readTextFileSync('deno.json')) }))" + ], + "output": "" + }, + { + "args": "cache --frozen=false add.ts", + "output": "[WILDCARD]" + }, + { + // sub.ts imports from an npm package + // that's not in the lockfile + "args": "run sub.ts", + "output": "frozen_new_dep_run.out", + "exitCode": 1 + }, + { + "args": "cache sub.ts", + "output": "frozen_new_dep_cache.out", + "exitCode": 1 + } + ] + }, + "non_analyzable_dynamic_npm": { "steps": [ {