diff --git a/cli/lockfile.rs b/cli/lockfile.rs index a2658918de..afbd8d2879 100644 --- a/cli/lockfile.rs +++ b/cli/lockfile.rs @@ -1,8 +1,12 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +#![allow(dead_code)] + use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; +use deno_core::serde::Deserialize; +use deno_core::serde::Serialize; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::ModuleSpecifier; @@ -16,9 +20,20 @@ use std::sync::Arc; use crate::tools::fmt::format_json; +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LockfileV2Content { + version: String, + // Mapping between URLs and their checksums + remote: BTreeMap, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct LockfileV1Content(BTreeMap); + #[derive(Debug, Clone)] -pub struct LockfileContent { - map: BTreeMap, +enum LockfileContent { + V1(LockfileV1Content), + V2(LockfileV2Content), } #[derive(Debug, Clone)] @@ -30,19 +45,33 @@ pub struct Lockfile { impl Lockfile { pub fn new(filename: PathBuf, write: bool) -> Result { - let map = if write { - BTreeMap::new() + // Writing a lock file always uses the new format. + let content = if write { + LockfileContent::V2(LockfileV2Content { + version: "2".to_string(), + remote: BTreeMap::new(), + }) } else { let s = std::fs::read_to_string(&filename).with_context(|| { format!("Unable to read lockfile: {}", filename.display()) })?; - serde_json::from_str(&s) - .context("Unable to parse contents of the lockfile")? + let value: serde_json::Value = serde_json::from_str(&s) + .context("Unable to parse contents of the lockfile")?; + let version = value.get("version").and_then(|v| v.as_str()); + if version == Some("2") { + let content: LockfileV2Content = + serde_json::from_value(value).context("Unable to parse lockfile")?; + LockfileContent::V2(content) + } else { + let content: LockfileV1Content = + serde_json::from_value(value).context("Unable to parse lockfile")?; + LockfileContent::V1(content) + } }; Ok(Lockfile { write, - content: LockfileContent { map }, + content, filename, }) } @@ -52,13 +81,19 @@ impl Lockfile { if !self.write { return Ok(()); } - let j = json!(&self.content.map); - let s = serde_json::to_string_pretty(&j).unwrap(); - let format_s = format_json(&s, &Default::default()) + let json_string = match &self.content { + LockfileContent::V1(c) => { + let j = json!(&c.0); + serde_json::to_string(&j).unwrap() + } + LockfileContent::V2(c) => serde_json::to_string(&c).unwrap(), + }; + + let format_s = format_json(&json_string, &Default::default()) .ok() .flatten() - .unwrap_or(s); + .unwrap_or(json_string); let mut f = std::fs::OpenOptions::new() .write(true) .create(true) @@ -85,11 +120,23 @@ impl Lockfile { if specifier.starts_with("file:") { return true; } - if let Some(lockfile_checksum) = self.content.map.get(specifier) { - let compiled_checksum = crate::checksum::gen(&[code.as_bytes()]); - lockfile_checksum == &compiled_checksum - } else { - false + match &self.content { + LockfileContent::V1(c) => { + if let Some(lockfile_checksum) = c.0.get(specifier) { + let compiled_checksum = crate::checksum::gen(&[code.as_bytes()]); + lockfile_checksum == &compiled_checksum + } else { + false + } + } + LockfileContent::V2(c) => { + if let Some(lockfile_checksum) = c.remote.get(specifier) { + let compiled_checksum = crate::checksum::gen(&[code.as_bytes()]); + lockfile_checksum == &compiled_checksum + } else { + false + } + } } } @@ -98,7 +145,14 @@ impl Lockfile { return; } let checksum = crate::checksum::gen(&[code.as_bytes()]); - self.content.map.insert(specifier.to_string(), checksum); + match &mut self.content { + LockfileContent::V1(c) => { + c.0.insert(specifier.to_string(), checksum); + } + LockfileContent::V2(c) => { + c.remote.insert(specifier.to_string(), checksum); + } + }; } } @@ -154,8 +208,11 @@ mod tests { let mut file = File::create(file_path).expect("write file fail"); let value: serde_json::Value = json!({ - "https://deno.land/std@0.71.0/textproto/mod.ts": "3118d7a42c03c242c5a49c2ad91c8396110e14acca1324e7aaefd31a999b71a4", - "https://deno.land/std@0.71.0/async/delay.ts": "35957d585a6e3dd87706858fb1d6b551cb278271b03f52c5a2cb70e65e00c26a" + "version": "2", + "remote": { + "https://deno.land/std@0.71.0/textproto/mod.ts": "3118d7a42c03c242c5a49c2ad91c8396110e14acca1324e7aaefd31a999b71a4", + "https://deno.land/std@0.71.0/async/delay.ts": "35957d585a6e3dd87706858fb1d6b551cb278271b03f52c5a2cb70e65e00c26a" + } }); file.write_all(value.to_string().as_bytes()).unwrap(); @@ -176,7 +233,11 @@ mod tests { let result = Lockfile::new(file_path, false).unwrap(); - let keys: Vec = result.content.map.keys().cloned().collect(); + let remote = match result.content { + LockfileContent::V2(c) => c.remote, + _ => unreachable!(), + }; + let keys: Vec = remote.keys().cloned().collect(); let expected_keys = vec![ String::from("https://deno.land/std@0.71.0/async/delay.ts"), String::from("https://deno.land/std@0.71.0/textproto/mod.ts"), @@ -198,7 +259,11 @@ mod tests { "Here is some source code", ); - let keys: Vec = lockfile.content.map.keys().cloned().collect(); + let remote = match lockfile.content { + LockfileContent::V2(c) => c.remote, + _ => unreachable!(), + }; + let keys: Vec = remote.keys().cloned().collect(); let expected_keys = vec![ String::from("https://deno.land/std@0.71.0/async/delay.ts"), String::from("https://deno.land/std@0.71.0/io/util.ts"), @@ -242,7 +307,7 @@ mod tests { let contents_json = serde_json::from_str::(&contents).unwrap(); - let object = contents_json.as_object().unwrap(); + let object = contents_json["remote"].as_object().unwrap(); assert_eq!( object diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs index 156940185b..71b26b356a 100644 --- a/cli/tests/integration/run_tests.rs +++ b/cli/tests/integration/run_tests.rs @@ -658,6 +658,40 @@ itest!(lock_check_err2 { http_server: true, }); +itest!(lock_v2_check_ok { + args: + "run --lock=run/lock_v2_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts", + output: "run/003_relative_import.ts.out", + http_server: true, +}); + +itest!(lock_v2_check_ok2 { + args: "run --lock=run/lock_v2_check_ok2.json run/019_media_types.ts", + output: "run/019_media_types.ts.out", + http_server: true, +}); + +itest!(lock_v2_dynamic_imports { + args: "run --lock=run/lock_v2_dynamic_imports.json --allow-read --allow-net http://127.0.0.1:4545/run/013_dynamic_import.ts", + output: "run/lock_v2_dynamic_imports.out", + exit_code: 10, + http_server: true, +}); + +itest!(lock_v2_check_err { + args: "run --lock=run/lock_v2_check_err.json http://127.0.0.1:4545/run/003_relative_import.ts", + output: "run/lock_v2_check_err.out", + exit_code: 10, + http_server: true, +}); + +itest!(lock_v2_check_err2 { + args: "run --lock=run/lock_v2_check_err2.json run/019_media_types.ts", + output: "run/lock_v2_check_err2.out", + exit_code: 10, + http_server: true, +}); + itest!(mts_dmts_mjs { args: "run subdir/import.mts", output: "run/mts_dmts_mjs.out", diff --git a/cli/tests/testdata/run/lock_v2_check_err.json b/cli/tests/testdata/run/lock_v2_check_err.json new file mode 100644 index 0000000000..6bd6491c68 --- /dev/null +++ b/cli/tests/testdata/run/lock_v2_check_err.json @@ -0,0 +1,7 @@ +{ + "version": "2", + "remote": { + "http://127.0.0.1:4545/subdir/print_hello.ts": "fa6692c8f9ff3fb107e773c3ece5274e9d08be282867a1e3ded1d9c00fcaa63c", + "http://127.0.0.1:4545/run/003_relative_import.ts": "bad" + } +} diff --git a/cli/tests/testdata/run/lock_v2_check_err.out b/cli/tests/testdata/run/lock_v2_check_err.out new file mode 100644 index 0000000000..28ad01cf44 --- /dev/null +++ b/cli/tests/testdata/run/lock_v2_check_err.out @@ -0,0 +1,3 @@ +[WILDCARD]The source code is invalid, as it does not match the expected hash in the lock file. + Specifier: http://127.0.0.1:4545/run/003_relative_import.ts + Lock file: run/lock_v2_check_err.json diff --git a/cli/tests/testdata/run/lock_v2_check_err2.json b/cli/tests/testdata/run/lock_v2_check_err2.json new file mode 100644 index 0000000000..30fbcdf4bf --- /dev/null +++ b/cli/tests/testdata/run/lock_v2_check_err2.json @@ -0,0 +1,13 @@ +{ + "version": "2", + "remote": { + "http://localhost:4545/subdir/mt_application_ecmascript.j2.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_application_x_javascript.j4.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_application_x_typescript.t4.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_text_ecmascript.j3.js": "bad", + "http://localhost:4545/subdir/mt_text_javascript.j1.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_text_typescript.t1.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_video_mp2t.t3.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_video_vdn.t2.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18" + } +} diff --git a/cli/tests/testdata/run/lock_v2_check_err2.out b/cli/tests/testdata/run/lock_v2_check_err2.out new file mode 100644 index 0000000000..3d82cba273 --- /dev/null +++ b/cli/tests/testdata/run/lock_v2_check_err2.out @@ -0,0 +1,3 @@ +[WILDCARD]The source code is invalid, as it does not match the expected hash in the lock file. + Specifier: http://localhost:4545/subdir/mt_text_ecmascript.j3.js + Lock file: run/lock_v2_check_err2.json diff --git a/cli/tests/testdata/run/lock_v2_check_ok.json b/cli/tests/testdata/run/lock_v2_check_ok.json new file mode 100644 index 0000000000..63bec862a7 --- /dev/null +++ b/cli/tests/testdata/run/lock_v2_check_ok.json @@ -0,0 +1,7 @@ +{ + "version": "2", + "remote": { + "http://127.0.0.1:4545/subdir/print_hello.ts": "fa6692c8f9ff3fb107e773c3ece5274e9d08be282867a1e3ded1d9c00fcaa63c", + "http://127.0.0.1:4545/run/003_relative_import.ts": "a1572e8fd2c2712b33f04aed2561505b5feb2c8696f1f2cded3de7127931b97e" + } +} diff --git a/cli/tests/testdata/run/lock_v2_check_ok2.json b/cli/tests/testdata/run/lock_v2_check_ok2.json new file mode 100644 index 0000000000..4356c9421a --- /dev/null +++ b/cli/tests/testdata/run/lock_v2_check_ok2.json @@ -0,0 +1,13 @@ +{ + "version": "2", + "remote": { + "http://localhost:4545/subdir/mt_application_ecmascript.j2.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_application_x_javascript.j4.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_application_x_typescript.t4.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_text_ecmascript.j3.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_text_javascript.j1.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_text_typescript.t1.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_video_mp2t.t3.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18", + "http://localhost:4545/subdir/mt_video_vdn.t2.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18" + } +} diff --git a/cli/tests/testdata/run/lock_v2_dynamic_imports.json b/cli/tests/testdata/run/lock_v2_dynamic_imports.json new file mode 100644 index 0000000000..eadbee272d --- /dev/null +++ b/cli/tests/testdata/run/lock_v2_dynamic_imports.json @@ -0,0 +1,9 @@ +{ + "version": "2", + "remote": { + "http://127.0.0.1:4545/run/013_dynamic_import.ts": "3f83e653329dc1f963761a986997d710b9763f667fc243eef89b3a5decacda30", + "http://127.0.0.1:4545/subdir/mod1.ts": "bfc1037b02c99abc20367f739bca7455813a5950066abd77965bff33b6eece0f", + "http://127.0.0.1:4545/subdir/print_hello.ts": "fa6692c8f9ff3fb107e773c3ece5274e9d08be282867a1e3ded1d9c00fcaa63c", + "http://127.0.0.1:4545/subdir/subdir2/mod2.ts": "bad" + } +} diff --git a/cli/tests/testdata/run/lock_v2_dynamic_imports.out b/cli/tests/testdata/run/lock_v2_dynamic_imports.out new file mode 100644 index 0000000000..36c2c9b5cc --- /dev/null +++ b/cli/tests/testdata/run/lock_v2_dynamic_imports.out @@ -0,0 +1,4 @@ +[WILDCARD] +error: The source code is invalid, as it does not match the expected hash in the lock file. + Specifier: http://127.0.0.1:4545/subdir/subdir2/mod2.ts + Lock file: run/lock_v2_dynamic_imports.json