perf(node): ensure cjs wrapper module has deterministic output (#24248)

This commit is contained in:
David Sherret 2024-06-18 17:24:18 -04:00 committed by GitHub
parent 7b5c514763
commit cba212b9c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 72 additions and 62 deletions

View File

@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::collections::BTreeSet;
use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;
@ -106,7 +107,8 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
.to_string(),
];
let mut all_exports = analysis.exports.into_iter().collect::<HashSet<_>>();
// use a BTreeSet to make the output deterministic for v8's code cache
let mut all_exports = analysis.exports.into_iter().collect::<BTreeSet<_>>();
if !analysis.reexports.is_empty() {
let mut errors = Vec::new();
@ -159,7 +161,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
&'a self,
entry_specifier: &url::Url,
reexports: Vec<String>,
all_exports: &mut HashSet<String>,
all_exports: &mut BTreeSet<String>,
// this goes through the modules concurrently, so collect
// the errors in order to be deterministic
errors: &mut Vec<anyhow::Error>,

View File

@ -5043,8 +5043,8 @@ fn run_etag_delete_source_cache() {
#[test]
fn code_cache_test() {
let deno_dir = TempDir::new();
let test_context = TestContextBuilder::new().use_temp_cwd().build();
let deno_dir = test_context.deno_dir();
let temp_dir = test_context.temp_dir();
temp_dir.write("main.js", "console.log('Hello World - A');");
@ -5052,17 +5052,14 @@ fn code_cache_test() {
{
let output = test_context
.new_command()
.env("DENO_DIR", deno_dir.path())
.arg("run")
.arg("-Ldebug")
.arg("main.js")
.args("run -Ldebug main.js")
.split_output()
.run();
output
.assert_stdout_matches_text("Hello World - A[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/main.js[WILDCARD]");
assert!(!output.stderr().contains("V8 code cache hit"));
assert_not_contains!(output.stderr(), "V8 code cache hit");
// Check that the code cache database exists.
let code_cache_path = deno_dir.path().join(CODE_CACHE_DB_FILE_NAME);
@ -5073,35 +5070,28 @@ fn code_cache_test() {
{
let output = test_context
.new_command()
.env("DENO_DIR", deno_dir.path())
.arg("run")
.arg("-Ldebug")
.arg("main.js")
.args("run -Ldebug main.js")
.split_output()
.run();
output
.assert_stdout_matches_text("Hello World - A[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]V8 code cache hit for ES module: file:///[WILDCARD]/main.js[WILDCARD]");
assert!(!output.stderr().contains("Updating V8 code cache"));
assert_not_contains!(output.stderr(), "Updating V8 code cache");
}
// Rerun with --no-code-cache.
{
let output = test_context
.new_command()
.env("DENO_DIR", deno_dir.path())
.arg("run")
.arg("-Ldebug")
.arg("--no-code-cache")
.arg("main.js")
.args("run -Ldebug --no-code-cache main.js")
.split_output()
.run();
output
.assert_stdout_matches_text("Hello World - A[WILDCARD]")
.skip_stderr_check();
assert!(!output.stderr().contains("V8 code cache"));
assert_not_contains!(output.stderr(), "V8 code cache");
}
// Modify the script, and make sure that the cache is rejected.
@ -5109,27 +5099,21 @@ fn code_cache_test() {
{
let output = test_context
.new_command()
.env("DENO_DIR", deno_dir.path())
.arg("run")
.arg("-Ldebug")
.arg("main.js")
.args("run -Ldebug main.js")
.split_output()
.run();
output
.assert_stdout_matches_text("Hello World - B[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/main.js[WILDCARD]");
assert!(!output.stderr().contains("V8 code cache hit"));
assert_not_contains!(output.stderr(), "V8 code cache hit");
}
}
#[test]
fn code_cache_npm_test() {
let deno_dir = TempDir::new();
let test_context = TestContextBuilder::new()
.use_temp_cwd()
.use_http_server()
.build();
let test_context = TestContextBuilder::for_npm().use_temp_cwd().build();
let deno_dir = test_context.deno_dir();
let temp_dir = test_context.temp_dir();
temp_dir.write(
"main.js",
@ -5140,12 +5124,7 @@ fn code_cache_npm_test() {
{
let output = test_context
.new_command()
.env("DENO_DIR", deno_dir.path())
.envs(env_vars_for_npm_tests())
.arg("run")
.arg("-Ldebug")
.arg("-A")
.arg("main.js")
.args("run -Ldebug -A main.js")
.split_output()
.run();
@ -5153,7 +5132,7 @@ fn code_cache_npm_test() {
.assert_stdout_matches_text("Hello World[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/main.js[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/chalk/5.[WILDCARD]/source/index.js[WILDCARD]");
assert!(!output.stderr().contains("V8 code cache hit"));
assert_not_contains!(output.stderr(), "V8 code cache hit");
// Check that the code cache database exists.
let code_cache_path = deno_dir.path().join(CODE_CACHE_DB_FILE_NAME);
@ -5164,12 +5143,7 @@ fn code_cache_npm_test() {
{
let output = test_context
.new_command()
.env("DENO_DIR", deno_dir.path())
.envs(env_vars_for_npm_tests())
.arg("run")
.arg("-Ldebug")
.arg("-A")
.arg("main.js")
.args("run -Ldebug -A main.js")
.split_output()
.run();
@ -5177,17 +5151,14 @@ fn code_cache_npm_test() {
.assert_stdout_matches_text("Hello World[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]V8 code cache hit for ES module: file:///[WILDCARD]/main.js[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]V8 code cache hit for ES module: file:///[WILDCARD]/chalk/5.[WILDCARD]/source/index.js[WILDCARD]");
assert!(!output.stderr().contains("Updating V8 code cache"));
assert_not_contains!(output.stderr(), "Updating V8 code cache");
}
}
#[test]
fn code_cache_npm_with_require_test() {
let deno_dir = TempDir::new();
let test_context = TestContextBuilder::new()
.use_temp_cwd()
.use_http_server()
.build();
let test_context = TestContextBuilder::for_npm().use_temp_cwd().build();
let deno_dir = test_context.deno_dir();
let temp_dir = test_context.temp_dir();
temp_dir.write(
"main.js",
@ -5198,12 +5169,7 @@ fn code_cache_npm_with_require_test() {
{
let output = test_context
.new_command()
.env("DENO_DIR", deno_dir.path())
.envs(env_vars_for_npm_tests())
.arg("run")
.arg("-Ldebug")
.arg("-A")
.arg("main.js")
.args("run -Ldebug -A main.js")
.split_output()
.run();
@ -5213,7 +5179,7 @@ fn code_cache_npm_with_require_test() {
.assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/autoprefixer/[WILDCARD]/autoprefixer.js[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for script: file:///[WILDCARD]/autoprefixer/[WILDCARD]/autoprefixer.js[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for script: file:///[WILDCARD]/browserslist/[WILDCARD]/index.js[WILDCARD]");
assert!(!output.stderr().contains("V8 code cache hit"));
assert_not_contains!(output.stderr(), "V8 code cache hit");
// Check that the code cache database exists.
let code_cache_path = deno_dir.path().join(CODE_CACHE_DB_FILE_NAME);
@ -5224,12 +5190,7 @@ fn code_cache_npm_with_require_test() {
{
let output = test_context
.new_command()
.env("DENO_DIR", deno_dir.path())
.envs(env_vars_for_npm_tests())
.arg("run")
.arg("-Ldebug")
.arg("-A")
.arg("main.js")
.args("run -Ldebug -A main.js")
.split_output()
.run();
@ -5239,7 +5200,54 @@ fn code_cache_npm_with_require_test() {
.assert_stderr_matches_text("[WILDCARD]V8 code cache hit for ES module: file:///[WILDCARD]/autoprefixer/[WILDCARD]/autoprefixer.js[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]V8 code cache hit for script: file:///[WILDCARD]/autoprefixer/[WILDCARD]/autoprefixer.js[WILDCARD]")
.assert_stderr_matches_text("[WILDCARD]V8 code cache hit for script: file:///[WILDCARD]/browserslist/[WILDCARD]/index.js[WILDCARD]");
assert!(!output.stderr().contains("Updating V8 code cache"));
assert_not_contains!(output.stderr(), "Updating V8 code cache");
}
}
#[test]
fn code_cache_npm_cjs_wrapper_module_many_exports() {
// The code cache was being invalidated because the CJS wrapper module
// had indeterministic output.
let test_context = TestContextBuilder::for_npm().use_temp_cwd().build();
let temp_dir = test_context.temp_dir();
temp_dir.write(
"main.js",
// this package has a few exports
"import { hello } from \"npm:@denotest/cjs-reexport-collision\";hello.sayHello();",
);
// First run with no prior cache.
{
let output = test_context
.new_command()
.args("run -Ldebug -A main.js")
.split_output()
.run();
assert_not_contains!(output.stderr(), "V8 code cache hit");
assert_contains!(output.stderr(), "Updating V8 code cache");
output.skip_stdout_check();
}
// 2nd run with cache.
{
let output = test_context
.new_command()
.args("run -Ldebug -A main.js")
.split_output()
.run();
assert_contains!(output.stderr(), "V8 code cache hit");
assert_not_contains!(output.stderr(), "Updating V8 code cache");
output.skip_stdout_check();
// should have two occurrences of this (one for entrypoint and one for wrapper module)
assert_eq!(
output
.stderr()
.split("V8 code cache hit for ES module")
.count(),
3
);
}
}