diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2022-10-01 06:15:56 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-01 12:15:56 +0200 |
commit | ecfafda9d8ead19cb35708f310e49176db2ec2b5 (patch) | |
tree | 222a52e0e5c06cc946d45240a6194c57b46a8724 /cli/node | |
parent | 1058d1868fcc28ce115d1cc231e66016308b76ce (diff) |
perf: node cjs & esm analysis cache (#16097)
This commit adds a cache for CJS and ESM analysis that is backed by an
SQLite file.
The connection to the DB is lazily created on first use, so shouldn't
have impact on the startup time.
Benched with running Vite
Deno v1.26:
```
$ deno task dev
Warning deno task is unstable and may drastically change in the future
Task dev deno run -A --unstable --node-modules-dir npm:vite
VITE v3.1.4 ready in 961 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
```
This branch:
```
../deno/target/release/deno task dev
Warning deno task is unstable and may drastically change in the future
Task dev deno run -A --unstable --node-modules-dir npm:vite
VITE v3.1.4 ready in 330 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
```
Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
Diffstat (limited to 'cli/node')
-rw-r--r-- | cli/node/analyze.rs | 40 | ||||
-rw-r--r-- | cli/node/mod.rs | 22 |
2 files changed, 50 insertions, 12 deletions
diff --git a/cli/node/analyze.rs b/cli/node/analyze.rs index ed2536390..9b449f675 100644 --- a/cli/node/analyze.rs +++ b/cli/node/analyze.rs @@ -12,6 +12,8 @@ use deno_core::error::AnyError; use deno_runtime::deno_node::NODE_GLOBAL_THIS_NAME; use std::fmt::Write; +use crate::cache::NodeAnalysisCache; + static NODE_GLOBALS: &[&str] = &[ "Buffer", "clearImmediate", @@ -32,18 +34,34 @@ static NODE_GLOBALS: &[&str] = &[ // `var` decls are taken into consideration. pub fn esm_code_with_node_globals( + analysis_cache: &NodeAnalysisCache, specifier: &ModuleSpecifier, code: String, ) -> Result<String, AnyError> { - let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { - specifier: specifier.to_string(), - text_info: deno_ast::SourceTextInfo::from_string(code), - media_type: deno_ast::MediaType::from(specifier), - capture_tokens: true, - scope_analysis: true, - maybe_syntax: None, - })?; - let top_level_decls = analyze_top_level_decls(&parsed_source)?; + let source_hash = NodeAnalysisCache::compute_source_hash(&code); + let text_info = deno_ast::SourceTextInfo::from_string(code); + let top_level_decls = if let Some(decls) = + analysis_cache.get_esm_analysis(specifier.as_str(), &source_hash) + { + HashSet::from_iter(decls) + } else { + let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { + specifier: specifier.to_string(), + text_info: text_info.clone(), + media_type: deno_ast::MediaType::from(specifier), + capture_tokens: true, + scope_analysis: true, + maybe_syntax: None, + })?; + let top_level_decls = analyze_top_level_decls(&parsed_source)?; + analysis_cache.set_esm_analysis( + specifier.as_str(), + &source_hash, + &top_level_decls.clone().into_iter().collect(), + ); + top_level_decls + }; + let mut globals = Vec::with_capacity(NODE_GLOBALS.len()); let has_global_this = top_level_decls.contains("globalThis"); for global in NODE_GLOBALS.iter() { @@ -64,7 +82,7 @@ pub fn esm_code_with_node_globals( write!(result, "var {0} = {1}.{0};", global, global_this_expr).unwrap(); } - let file_text = parsed_source.text_info().text_str(); + let file_text = text_info.text_str(); // strip the shebang let file_text = if file_text.starts_with("#!/") { let start_index = file_text.find('\n').unwrap_or(file_text.len()); @@ -148,6 +166,7 @@ mod tests { #[test] fn test_esm_code_with_node_globals() { let r = esm_code_with_node_globals( + &NodeAnalysisCache::new(None), &ModuleSpecifier::parse("https://example.com/foo/bar.js").unwrap(), "export const x = 1;".to_string(), ) @@ -163,6 +182,7 @@ mod tests { #[test] fn test_esm_code_with_node_globals_with_shebang() { let r = esm_code_with_node_globals( + &NodeAnalysisCache::new(None), &ModuleSpecifier::parse("https://example.com/foo/bar.js").unwrap(), "#!/usr/bin/env node\nexport const x = 1;".to_string(), ) diff --git a/cli/node/mod.rs b/cli/node/mod.rs index 3ea263b04..2e431eaa0 100644 --- a/cli/node/mod.rs +++ b/cli/node/mod.rs @@ -5,6 +5,7 @@ use std::collections::VecDeque; use std::path::Path; use std::path::PathBuf; +use crate::cache::NodeAnalysisCache; use crate::deno_std::CURRENT_STD_URL; use deno_ast::CjsAnalysis; use deno_ast::MediaType; @@ -734,12 +735,20 @@ pub fn translate_cjs_to_esm( code: String, media_type: MediaType, npm_resolver: &NpmPackageResolver, + node_analysis_cache: &NodeAnalysisCache, ) -> Result<String, AnyError> { fn perform_cjs_analysis( + analysis_cache: &NodeAnalysisCache, specifier: &str, media_type: MediaType, code: String, ) -> Result<CjsAnalysis, AnyError> { + let source_hash = NodeAnalysisCache::compute_source_hash(&code); + if let Some(analysis) = + analysis_cache.get_cjs_analysis(specifier, &source_hash) + { + return Ok(analysis); + } let parsed_source = deno_ast::parse_script(deno_ast::ParseParams { specifier: specifier.to_string(), text_info: deno_ast::SourceTextInfo::new(code.into()), @@ -748,7 +757,10 @@ pub fn translate_cjs_to_esm( scope_analysis: false, maybe_syntax: None, })?; - Ok(parsed_source.analyze_cjs()) + let analysis = parsed_source.analyze_cjs(); + analysis_cache.set_cjs_analysis(specifier, &source_hash, &analysis); + + Ok(analysis) } let mut temp_var_count = 0; @@ -758,7 +770,12 @@ pub fn translate_cjs_to_esm( r#"const require = Deno[Deno.internal].require.Module.createRequire(import.meta.url);"#.to_string(), ]; - let analysis = perform_cjs_analysis(specifier.as_str(), media_type, code)?; + let analysis = perform_cjs_analysis( + node_analysis_cache, + specifier.as_str(), + media_type, + code, + )?; let mut all_exports = analysis .exports @@ -804,6 +821,7 @@ pub fn translate_cjs_to_esm( { let analysis = perform_cjs_analysis( + node_analysis_cache, reexport_specifier.as_str(), reexport_file.media_type, reexport_file.source.to_string(), |