Skip to content

Commit 28aa489

Browse files
authored
feat(compile): unstable npm and node specifier support (#19005)
This is the initial support for npm and node specifiers in `deno compile`. The npm packages are included in the binary and read from it via a virtual file system. This also supports the `--node-modules-dir` flag, dependencies specified in a package.json, and npm binary commands (ex. `deno compile --unstable npm:cowsay`) Closes #16632
1 parent 5fd74bf commit 28aa489

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2733
-261
lines changed

Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ lazy-regex.workspace = true
8484
libc.workspace = true
8585
log = { workspace = true, features = ["serde"] }
8686
lsp-types.workspace = true
87-
monch = "=0.4.1"
87+
monch = "=0.4.2"
8888
notify.workspace = true
8989
once_cell.workspace = true
9090
os_pipe.workspace = true

cli/args/flags.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,11 @@ impl Flags {
527527
.ok()
528528
}
529529
Task(_) | Check(_) | Coverage(_) | Cache(_) | Info(_) | Eval(_)
530-
| Test(_) | Bench(_) | Repl(_) => std::env::current_dir().ok(),
531-
_ => None,
530+
| Test(_) | Bench(_) | Repl(_) | Compile(_) => {
531+
std::env::current_dir().ok()
532+
}
533+
Bundle(_) | Completions(_) | Doc(_) | Fmt(_) | Init(_) | Install(_)
534+
| Uninstall(_) | Lsp | Lint(_) | Types | Upgrade(_) | Vendor(_) => None,
532535
}
533536
}
534537

cli/args/mod.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub use config_file::TsTypeLib;
3333
pub use flags::*;
3434
pub use lockfile::Lockfile;
3535
pub use lockfile::LockfileError;
36+
pub use package_json::PackageJsonDepsProvider;
3637

3738
use deno_ast::ModuleSpecifier;
3839
use deno_core::anyhow::anyhow;
@@ -556,7 +557,7 @@ struct CliOptionOverrides {
556557
import_map_specifier: Option<Option<ModuleSpecifier>>,
557558
}
558559

559-
/// Holds the resolved options of many sources used by sub commands
560+
/// Holds the resolved options of many sources used by subcommands
560561
/// and provides some helper function for creating common objects.
561562
pub struct CliOptions {
562563
// the source of the options is a detail the rest of the
@@ -1303,6 +1304,16 @@ fn has_flag_env_var(name: &str) -> bool {
13031304
matches!(value.as_ref().map(|s| s.as_str()), Ok("1"))
13041305
}
13051306

1307+
pub fn npm_pkg_req_ref_to_binary_command(
1308+
req_ref: &NpmPackageReqReference,
1309+
) -> String {
1310+
let binary_name = req_ref
1311+
.sub_path
1312+
.as_deref()
1313+
.unwrap_or(req_ref.req.name.as_str());
1314+
binary_name.to_string()
1315+
}
1316+
13061317
#[cfg(test)]
13071318
mod test {
13081319
use super::*;

cli/args/package_json.rs

+27
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,33 @@ pub enum PackageJsonDepValueParseError {
2828
pub type PackageJsonDeps =
2929
BTreeMap<String, Result<NpmPackageReq, PackageJsonDepValueParseError>>;
3030

31+
#[derive(Debug, Default)]
32+
pub struct PackageJsonDepsProvider(Option<PackageJsonDeps>);
33+
34+
impl PackageJsonDepsProvider {
35+
pub fn new(deps: Option<PackageJsonDeps>) -> Self {
36+
Self(deps)
37+
}
38+
39+
pub fn deps(&self) -> Option<&PackageJsonDeps> {
40+
self.0.as_ref()
41+
}
42+
43+
pub fn reqs(&self) -> Vec<&NpmPackageReq> {
44+
match &self.0 {
45+
Some(deps) => {
46+
let mut package_reqs = deps
47+
.values()
48+
.filter_map(|r| r.as_ref().ok())
49+
.collect::<Vec<_>>();
50+
package_reqs.sort(); // deterministic resolution
51+
package_reqs
52+
}
53+
None => Vec::new(),
54+
}
55+
}
56+
}
57+
3158
/// Gets an application level package.json's npm package requirements.
3259
///
3360
/// Note that this function is not general purpose. It is specifically for

cli/factory.rs

+39-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
22

3+
use crate::args::npm_pkg_req_ref_to_binary_command;
34
use crate::args::CliOptions;
45
use crate::args::DenoSubcommand;
56
use crate::args::Flags;
67
use crate::args::Lockfile;
8+
use crate::args::PackageJsonDepsProvider;
79
use crate::args::StorageKeyResolver;
810
use crate::args::TsConfigType;
911
use crate::cache::Caches;
@@ -30,6 +32,7 @@ use crate::npm::NpmCache;
3032
use crate::npm::NpmResolution;
3133
use crate::npm::PackageJsonDepsInstaller;
3234
use crate::resolver::CliGraphResolver;
35+
use crate::standalone::DenoCompileBinaryWriter;
3336
use crate::tools::check::TypeChecker;
3437
use crate::util::progress_bar::ProgressBar;
3538
use crate::util::progress_bar::ProgressBarStyle;
@@ -151,6 +154,7 @@ struct CliFactoryServices {
151154
npm_cache: Deferred<Arc<NpmCache>>,
152155
npm_resolver: Deferred<Arc<CliNpmResolver>>,
153156
npm_resolution: Deferred<Arc<NpmResolution>>,
157+
package_json_deps_provider: Deferred<Arc<PackageJsonDepsProvider>>,
154158
package_json_deps_installer: Deferred<Arc<PackageJsonDepsInstaller>>,
155159
text_only_progress_bar: Deferred<ProgressBar>,
156160
type_checker: Deferred<Arc<TypeChecker>>,
@@ -301,15 +305,17 @@ impl CliFactory {
301305
.npm_resolver
302306
.get_or_try_init_async(async {
303307
let npm_resolution = self.npm_resolution().await?;
308+
let fs = self.fs().clone();
304309
let npm_fs_resolver = create_npm_fs_resolver(
305-
self.fs().clone(),
310+
fs.clone(),
306311
self.npm_cache()?.clone(),
307312
self.text_only_progress_bar(),
308313
CliNpmRegistryApi::default_url().to_owned(),
309314
npm_resolution.clone(),
310315
self.options.node_modules_dir_path(),
311316
);
312317
Ok(Arc::new(CliNpmResolver::new(
318+
fs.clone(),
313319
npm_resolution.clone(),
314320
npm_fs_resolver,
315321
self.maybe_lockfile().as_ref().cloned(),
@@ -318,19 +324,25 @@ impl CliFactory {
318324
.await
319325
}
320326

327+
pub fn package_json_deps_provider(&self) -> &Arc<PackageJsonDepsProvider> {
328+
self.services.package_json_deps_provider.get_or_init(|| {
329+
Arc::new(PackageJsonDepsProvider::new(
330+
self.options.maybe_package_json_deps(),
331+
))
332+
})
333+
}
334+
321335
pub async fn package_json_deps_installer(
322336
&self,
323337
) -> Result<&Arc<PackageJsonDepsInstaller>, AnyError> {
324338
self
325339
.services
326340
.package_json_deps_installer
327341
.get_or_try_init_async(async {
328-
let npm_api = self.npm_api()?;
329-
let npm_resolution = self.npm_resolution().await?;
330342
Ok(Arc::new(PackageJsonDepsInstaller::new(
331-
npm_api.clone(),
332-
npm_resolution.clone(),
333-
self.options.maybe_package_json_deps(),
343+
self.package_json_deps_provider().clone(),
344+
self.npm_api()?.clone(),
345+
self.npm_resolution().await?.clone(),
334346
)))
335347
})
336348
.await
@@ -365,6 +377,7 @@ impl CliFactory {
365377
self.options.no_npm(),
366378
self.npm_api()?.clone(),
367379
self.npm_resolution().await?.clone(),
380+
self.package_json_deps_provider().clone(),
368381
self.package_json_deps_installer().await?.clone(),
369382
)))
370383
})
@@ -535,6 +548,21 @@ impl CliFactory {
535548
self.services.cjs_resolutions.get_or_init(Default::default)
536549
}
537550

551+
pub async fn create_compile_binary_writer(
552+
&self,
553+
) -> Result<DenoCompileBinaryWriter, AnyError> {
554+
Ok(DenoCompileBinaryWriter::new(
555+
self.file_fetcher()?,
556+
self.http_client(),
557+
self.deno_dir()?,
558+
self.npm_api()?,
559+
self.npm_cache()?,
560+
self.npm_resolver().await?,
561+
self.npm_resolution().await?,
562+
self.package_json_deps_provider(),
563+
))
564+
}
565+
538566
/// Gets a function that can be used to create a CliMainWorkerFactory
539567
/// for a file watcher.
540568
pub async fn create_cli_main_worker_factory_func(
@@ -572,6 +600,7 @@ impl CliFactory {
572600
NpmModuleLoader::new(
573601
cjs_resolutions.clone(),
574602
node_code_translator.clone(),
603+
fs.clone(),
575604
node_resolver.clone(),
576605
),
577606
)),
@@ -587,6 +616,7 @@ impl CliFactory {
587616
&self,
588617
) -> Result<CliMainWorkerFactory, AnyError> {
589618
let node_resolver = self.node_resolver().await?;
619+
let fs = self.fs();
590620
Ok(CliMainWorkerFactory::new(
591621
StorageKeyResolver::from_options(&self.options),
592622
self.npm_resolver().await?.clone(),
@@ -603,6 +633,7 @@ impl CliFactory {
603633
NpmModuleLoader::new(
604634
self.cjs_resolutions().clone(),
605635
self.node_code_translator().await?.clone(),
636+
fs.clone(),
606637
node_resolver.clone(),
607638
),
608639
)),
@@ -637,11 +668,8 @@ impl CliFactory {
637668
if let Ok(pkg_ref) = NpmPackageReqReference::from_str(&flags.script) {
638669
// if the user ran a binary command, we'll need to set process.argv[0]
639670
// to be the name of the binary command instead of deno
640-
let binary_name = pkg_ref
641-
.sub_path
642-
.as_deref()
643-
.unwrap_or(pkg_ref.req.name.as_str());
644-
maybe_binary_command_name = Some(binary_name.to_string());
671+
maybe_binary_command_name =
672+
Some(npm_pkg_req_ref_to_binary_command(&pkg_ref));
645673
}
646674
}
647675
maybe_binary_command_name

cli/graph_util.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,10 @@ pub fn error_for_any_npm_specifier(
345345
for module in graph.modules() {
346346
match module {
347347
Module::Npm(module) => {
348-
bail!("npm specifiers have not yet been implemented for this sub command (https://github.com/denoland/deno/issues/15960). Found: {}", module.specifier)
348+
bail!("npm specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: {}", module.specifier)
349349
}
350350
Module::Node(module) => {
351-
bail!("Node specifiers have not yet been implemented for this sub command (https://github.com/denoland/deno/issues/15960). Found: node:{}", module.module_name)
351+
bail!("Node specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: node:{}", module.module_name)
352352
}
353353
Module::Esm(_) | Module::Json(_) | Module::External(_) => {}
354354
}

cli/lsp/documents.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use deno_semver::npm::NpmPackageReqReference;
4646
use indexmap::IndexMap;
4747
use lsp::Url;
4848
use once_cell::sync::Lazy;
49+
use package_json::PackageJsonDepsProvider;
4950
use std::collections::BTreeMap;
5051
use std::collections::HashMap;
5152
use std::collections::HashSet;
@@ -1218,17 +1219,20 @@ impl Documents {
12181219
maybe_jsx_config.as_ref(),
12191220
maybe_package_json_deps.as_ref(),
12201221
);
1222+
let deps_provider =
1223+
Arc::new(PackageJsonDepsProvider::new(maybe_package_json_deps));
12211224
let deps_installer = Arc::new(PackageJsonDepsInstaller::new(
1225+
deps_provider.clone(),
12221226
npm_registry_api.clone(),
12231227
npm_resolution.clone(),
1224-
maybe_package_json_deps,
12251228
));
12261229
self.resolver = Arc::new(CliGraphResolver::new(
12271230
maybe_jsx_config,
12281231
maybe_import_map,
12291232
false,
12301233
npm_registry_api,
12311234
npm_resolution,
1235+
deps_provider,
12321236
deps_installer,
12331237
));
12341238
self.imports = Arc::new(

cli/lsp/language_server.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,9 @@ fn create_lsp_structs(
457457
));
458458
let resolution =
459459
Arc::new(NpmResolution::from_serialized(api.clone(), None, None));
460+
let fs = Arc::new(deno_fs::RealFs);
460461
let fs_resolver = create_npm_fs_resolver(
461-
Arc::new(deno_fs::RealFs),
462+
fs.clone(),
462463
npm_cache.clone(),
463464
&progress_bar,
464465
registry_url.clone(),
@@ -468,7 +469,12 @@ fn create_lsp_structs(
468469
(
469470
api,
470471
npm_cache,
471-
Arc::new(CliNpmResolver::new(resolution.clone(), fs_resolver, None)),
472+
Arc::new(CliNpmResolver::new(
473+
fs,
474+
resolution.clone(),
475+
fs_resolver,
476+
None,
477+
)),
472478
resolution,
473479
)
474480
}
@@ -711,6 +717,7 @@ impl Inner {
711717
));
712718
let node_fs = Arc::new(deno_fs::RealFs);
713719
let npm_resolver = Arc::new(CliNpmResolver::new(
720+
node_fs.clone(),
714721
npm_resolution.clone(),
715722
create_npm_fs_resolver(
716723
node_fs.clone(),

cli/main.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ static GLOBAL: Jemalloc = Jemalloc;
3636
use crate::args::flags_from_vec;
3737
use crate::args::DenoSubcommand;
3838
use crate::args::Flags;
39-
use crate::resolver::CliGraphResolver;
4039
use crate::util::display;
4140
use crate::util::v8::get_v8_flags_from_env;
4241
use crate::util::v8::init_v8_flags;
@@ -97,7 +96,7 @@ async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
9796
Ok(0)
9897
}
9998
DenoSubcommand::Compile(compile_flags) => {
100-
tools::standalone::compile(flags, compile_flags).await?;
99+
tools::compile::compile(flags, compile_flags).await?;
101100
Ok(0)
102101
}
103102
DenoSubcommand::Coverage(coverage_flags) => {

0 commit comments

Comments
 (0)