diff --git a/Cargo.lock b/Cargo.lock index 5b24c9f46b..06aa9d018d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1908,6 +1908,15 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "deno_features" +version = "0.0.0" +dependencies = [ + "deno_core", + "serde", + "serde_json", +] + [[package]] name = "deno_fetch" version = "0.227.0" @@ -2562,6 +2571,7 @@ dependencies = [ "deno_cron", "deno_crypto", "deno_error", + "deno_features", "deno_fetch", "deno_ffi", "deno_fs", diff --git a/Cargo.toml b/Cargo.toml index f98b7b4736..2d450a103a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ members = [ "resolvers/node", "resolvers/npm_cache", "runtime", + "runtime/features", "runtime/permissions", "tests", "tests/ffi", @@ -107,6 +108,7 @@ denort_helper = { version = "0.1.0", path = "./ext/rt_helper" } # workspace libraries deno_bench_util = { version = "0.197.0", path = "./bench_util" } +deno_features = { version = "0.0.0", path = "./runtime/features" } deno_lib = { version = "2.3.0-rc.3", path = "./cli/lib" } deno_npm_cache = { version = "0.22.0", path = "./resolvers/npm_cache" } deno_permissions = { version = "0.62.0", path = "./runtime/permissions" } diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 03cbb3874f..de9ef756e5 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -38,6 +38,7 @@ use deno_npm::NpmSystemInfo; use deno_path_util::normalize_path; use deno_path_util::url_to_file_path; use deno_runtime::deno_permissions::SysDescriptor; +use deno_runtime::UnstableFeatureKind; use deno_telemetry::OtelConfig; use deno_telemetry::OtelConsoleConfig; use deno_telemetry::OtelPropagators; @@ -4428,114 +4429,41 @@ impl CommandExt for Command { .action(ArgAction::SetTrue) .hide(matches!(cfg, UnstableArgsConfig::None)) .display_order(next_display_order()) - ).arg( - Arg::new("unstable-bare-node-builtins") - .long("unstable-bare-node-builtins") - .help("Enable unstable bare node builtins feature") - .env("DENO_UNSTABLE_BARE_NODE_BUILTINS") - .value_parser(FalseyValueParser::new()) - .action(ArgAction::SetTrue) - .hide(true) - .long_help(match cfg { - UnstableArgsConfig::None => None, - UnstableArgsConfig::ResolutionOnly - | UnstableArgsConfig::ResolutionAndRuntime => Some("true"), - }) - .help_heading(UNSTABLE_HEADING) - .display_order(next_display_order()), - ).arg( - Arg::new("unstable-detect-cjs") - .long("unstable-detect-cjs") - .help("Treats ambiguous .js, .jsx, .ts, .tsx files as CommonJS modules in more cases") - .value_parser(FalseyValueParser::new()) - .action(ArgAction::SetTrue) - .hide(true) - .long_help(match cfg { - UnstableArgsConfig::None => None, - UnstableArgsConfig::ResolutionOnly - | UnstableArgsConfig::ResolutionAndRuntime => Some("true"), - }) - .help_heading(UNSTABLE_HEADING) - .display_order(next_display_order()) - ).arg( - Arg::new("unstable-byonm") - .long("unstable-byonm") - .value_parser(FalseyValueParser::new()) - .action(ArgAction::SetTrue) - .hide(true) - .help_heading(UNSTABLE_HEADING) - .display_order(next_display_order()), - ).arg( - Arg::new("unstable-lazy-dynamic-imports") - .long("unstable-lazy-dynamic-imports") - .help("Lazily loads statically analyzable dynamic imports when not running with type checking. Warning: This may change the order of semver specifier resolution.") - .env("DENO_UNSTABLE_LAZY_DYNAMIC_IMPORTS") - .value_parser(FalseyValueParser::new()) - .action(ArgAction::SetTrue) - .hide(true) - .long_help(match cfg { - UnstableArgsConfig::None => None, - UnstableArgsConfig::ResolutionOnly | UnstableArgsConfig::ResolutionAndRuntime => Some("true") - }) - .help_heading(UNSTABLE_HEADING) - .display_order(next_display_order()) - ).arg( - Arg::new("unstable-sloppy-imports") - .long("unstable-sloppy-imports") - .help("Enable unstable resolving of specifiers by extension probing, .js to .ts, and directory probing") - .env("DENO_UNSTABLE_SLOPPY_IMPORTS") - .value_parser(FalseyValueParser::new()) - .action(ArgAction::SetTrue) - .hide(true) - .long_help(match cfg { - UnstableArgsConfig::None => None, - UnstableArgsConfig::ResolutionOnly | UnstableArgsConfig::ResolutionAndRuntime => Some("true") - }) - .help_heading(UNSTABLE_HEADING) - .display_order(next_display_order()) - ).arg( - Arg::new("unstable-npm-lazy-caching") - .long("unstable-npm-lazy-caching") - .help("Enable unstable lazy caching of npm dependencies, downloading them only as needed (disabled: all npm packages in package.json are installed on startup; enabled: only npm packages that are actually referenced in an import are installed") - .env("DENO_UNSTABLE_NPM_LAZY_CACHING") - .value_parser(FalseyValueParser::new()) - .action(ArgAction::SetTrue) - .hide(true) - .help_heading(UNSTABLE_HEADING) - .display_order(next_display_order()), - ).arg( - Arg::new("unstable-lockfile-v5") - .long("unstable-lockfile-v5") - .help("Enable unstable lockfile v5") - .env("DENO_UNSTABLE_LOCKFILE_V5") - .value_parser(FalseyValueParser::new()) - .action(ArgAction::SetTrue) - .help_heading(UNSTABLE_HEADING) - .hide(true) - .display_order(next_display_order()), ); - for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() { - cmd = cmd.arg( - Arg::new(format!("unstable-{}", granular_flag.name)) - .long(format!("unstable-{}", granular_flag.name)) - .help(granular_flag.help_text) - .action(ArgAction::SetTrue) - .hide(true) - .help_heading(UNSTABLE_HEADING) - // we don't render long help, so using it here as a sort of metadata - .long_help(if granular_flag.show_in_help { - match cfg { - UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => { - None - } - UnstableArgsConfig::ResolutionAndRuntime => Some("true"), - } - } else { - None - }) - .display_order(next_display_order()), - ); + for feature in crate::UNSTABLE_FEATURES.iter() { + let mut arg = Arg::new(feature.flag_name) + .long(feature.flag_name) + .help(feature.help_text) + .action(ArgAction::SetTrue) + .value_parser(FalseyValueParser::new()) + .hide(true) + .help_heading(UNSTABLE_HEADING) + .display_order(next_display_order()); + + // TODO(bartlomieju): + // Value of `.long_help()` is actuall a metadata. It should be rewritten to use + // Clap's `ArgExt` instead + let mut long_help_val = None; + + if feature.show_in_help { + if matches!(cfg, UnstableArgsConfig::ResolutionOnly) + && matches!(feature.kind, UnstableFeatureKind::Cli) + { + long_help_val = Some("true"); + } + + if matches!(cfg, UnstableArgsConfig::ResolutionAndRuntime) { + long_help_val = Some("true"); + } + } + + arg = arg.long_help(long_help_val); + if let Some(env_var_name) = feature.env_var { + arg = arg.env(env_var_name); + } + + cmd = cmd.arg(arg); } cmd @@ -6142,6 +6070,7 @@ fn unstable_args_parse( flags.unstable_config.legacy_flag_enabled = true; } + // TODO(bartlomieju): this should be factored out since these are configured via UNSTABLE_FEATURES flags.unstable_config.bare_node_builtins = matches.get_flag("unstable-bare-node-builtins"); flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs"); @@ -6153,12 +6082,12 @@ fn unstable_args_parse( matches.get_flag("unstable-npm-lazy-caching"); if matches!(cfg, UnstableArgsConfig::ResolutionAndRuntime) { - for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS { - if matches.get_flag(&format!("unstable-{}", granular_flag.name)) { + for feature in crate::UNSTABLE_FEATURES { + if matches.get_flag(feature.flag_name) { flags .unstable_config .features - .push(granular_flag.name.to_string()); + .push(feature.name.to_string()); } } } @@ -11752,7 +11681,7 @@ mod tests { unstable_config: UnstableConfig { bare_node_builtins: true, sloppy_imports: false, - features: svec!["ffi", "worker-options"], + features: svec!["bare-node-builtins", "ffi", "worker-options"], ..Default::default() }, ..Flags::default() diff --git a/cli/args/mod.rs b/cli/args/mod.rs index c892c6d2c8..4031134d69 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1116,21 +1116,10 @@ impl CliOptions { .collect::>(); if !unstable_features.is_empty() { - let all_valid_unstable_flags: Vec<&str> = crate::UNSTABLE_GRANULAR_FLAGS + let all_valid_unstable_flags: Vec<&str> = crate::UNSTABLE_FEATURES .iter() - .map(|granular_flag| granular_flag.name) - .chain([ - "byonm", - "bare-node-builtins", - "detect-cjs", - "fmt-component", - "fmt-sql", - "lazy-dynamic-imports", - "npm-lazy-caching", - "npm-patch", - "sloppy-imports", - "lockfile-v5", - ]) + .map(|feature| feature.name) + .chain(["fmt-component", "fmt-sql", "npm-lazy-caching", "npm-patch"]) .collect(); // check and warn if the unstable flag of config file isn't supported, by diff --git a/cli/factory.rs b/cli/factory.rs index 22a971acc5..95f49d53c9 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -1142,9 +1142,9 @@ impl CliFactory { let mut checker = FeatureChecker::default(); checker.set_exit_cb(Box::new(crate::unstable_exit_cb)); let unstable_features = cli_options.unstable_features(); - for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS { - if unstable_features.contains(&granular_flag.name.to_string()) { - checker.enable_feature(granular_flag.name); + for feature in crate::UNSTABLE_FEATURES { + if unstable_features.contains(&feature.name.to_string()) { + checker.enable_feature(feature.name); } } diff --git a/cli/lib/worker.rs b/cli/lib/worker.rs index eb77b3bff1..a3908f7103 100644 --- a/cli/lib/worker.rs +++ b/cli/lib/worker.rs @@ -45,7 +45,7 @@ use deno_runtime::worker::WorkerServiceOptions; use deno_runtime::BootstrapOptions; use deno_runtime::WorkerExecutionMode; use deno_runtime::WorkerLogLevel; -use deno_runtime::UNSTABLE_GRANULAR_FLAGS; +use deno_runtime::UNSTABLE_FEATURES; use node_resolver::errors::ResolvePkgJsonBinExportError; use node_resolver::UrlOrPath; use url::Url; @@ -216,11 +216,10 @@ impl LibWorkerFactorySharedState { &self, feature_checker: &FeatureChecker, ) -> Vec { - let mut unstable_features = - Vec::with_capacity(UNSTABLE_GRANULAR_FLAGS.len()); - for granular_flag in UNSTABLE_GRANULAR_FLAGS { - if feature_checker.check(granular_flag.name) { - unstable_features.push(granular_flag.id); + let mut unstable_features = Vec::with_capacity(UNSTABLE_FEATURES.len()); + for feature in UNSTABLE_FEATURES { + if feature_checker.check(feature.name) { + unstable_features.push(feature.id); } } unstable_features diff --git a/cli/main.rs b/cli/main.rs index 41257a1cd5..cc44921d73 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -49,7 +49,7 @@ use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_runtime::fmt_errors::format_js_error; use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; use deno_runtime::WorkerExecutionMode; -pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; +pub use deno_runtime::UNSTABLE_FEATURES; use deno_telemetry::OtelConfig; use deno_terminal::colors; use factory::CliFactory; diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 54a5dda76b..614f38d56d 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -52,6 +52,7 @@ deno_core.workspace = true deno_cron.workspace = true deno_crypto.workspace = true deno_error.workspace = true +deno_features.workspace = true deno_fetch.workspace = true deno_ffi.workspace = true deno_fs = { workspace = true, features = ["sync_fs"] } diff --git a/runtime/features/Cargo.toml b/runtime/features/Cargo.toml new file mode 100644 index 0000000000..86d440aeed --- /dev/null +++ b/runtime/features/Cargo.toml @@ -0,0 +1,21 @@ +# Copyright 2018-2025 the Deno authors. MIT license. + +[package] +name = "deno_features" +version = "0.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +description = "Provides definitions of Deno unstable features." + +[lib] +name = "deno_features" +path = "lib.rs" + +[dependencies] +deno_core.workspace = true + +[build-dependencies] +serde.workspace = true +serde_json.workspace = true diff --git a/runtime/features/build.rs b/runtime/features/build.rs new file mode 100644 index 0000000000..6f92fc097b --- /dev/null +++ b/runtime/features/build.rs @@ -0,0 +1,102 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +#![allow(clippy::disallowed_methods)] + +mod data; +mod structs; + +fn main() { + let crate_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + + let mut js_list = String::from( + "// Copyright 2018-2025 the Deno authors. MIT license. + +/** + * Don't modify this file manually. + * + * This file is auto-generated by the build script, modify `data.rs` instead. + */ + +export const unstableIds = { +", + ); + + let mut rs_list = String::from( + "// Copyright 2018-2025 the Deno authors. MIT license. + +/// Don't modify this file manually. +/// +/// This file is auto-generated by the build script, modify `data.rs` instead. +use crate::structs::UnstableFeatureDefinition; +use crate::structs::UnstableFeatureKind; + +pub static UNSTABLE_FEATURES: &[UnstableFeatureDefinition] = &[", + ); + + let mut descriptions = data::FEATURE_DESCRIPTIONS.to_vec(); + descriptions.sort_by_key(|desc| desc.name); + + for (id, feature) in descriptions.into_iter().enumerate() { + let flag_name = format!("unstable-{}", feature.name); + let feature_kind = match feature.kind { + structs::UnstableFeatureKind::Cli => "UnstableFeatureKind::Cli", + structs::UnstableFeatureKind::Runtime => "UnstableFeatureKind::Runtime", + }; + let env_var = match feature.env_var { + Some(var_name) => format!(r#"Some("{}")"#, var_name), + None => "None".to_string(), + }; + + rs_list += &format!( + r#" UnstableFeatureDefinition {{ + name: "{}", + flag_name: "{}", + help_text: "{}", + show_in_help: {}, + id: {}, + kind: {}, + env_var: {}, + config_file_option: "{}", + }}, +"#, + feature.name, + flag_name, + feature.help_text, + feature.show_in_help, + id, + feature_kind, + env_var, + match feature.config_option { + data::ConfigFileOption::SameAsFlagName => feature.name, + data::ConfigFileOption::Renamed(alias) => alias, + } + ); + + if matches!(feature.kind, structs::UnstableFeatureKind::Runtime) { + let camel = camel_case(feature.name); + js_list += &format!(" {}: {},\n", camel, id); + } + } + + js_list += "};\n"; + rs_list += "];\n"; + + std::fs::write(crate_dir.join("gen.js"), js_list).unwrap(); + std::fs::write(crate_dir.join("gen.rs"), rs_list).unwrap(); +} + +fn camel_case(name: &str) -> String { + let mut output = String::new(); + let mut upper = false; + for c in name.chars() { + if c == '-' { + upper = true; + } else if upper { + upper = false; + output.push(c.to_ascii_uppercase()); + } else { + output.push(c); + } + } + output +} diff --git a/runtime/features/data.rs b/runtime/features/data.rs new file mode 100644 index 0000000000..87f0c78092 --- /dev/null +++ b/runtime/features/data.rs @@ -0,0 +1,211 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +// NOTE(bartlomieju): some fields are marked as never read, even though they are +// actually used in the CLI. +#![allow(dead_code)] + +use crate::structs::UnstableFeatureKind; + +#[derive(Clone, Debug)] +pub enum ConfigFileOption { + SameAsFlagName, + Renamed(&'static str), +} + +#[derive(Clone, Debug)] +pub struct UnstableFeatureDescription { + pub name: &'static str, + pub help_text: &'static str, + // TODO(bartlomieju): is it needed? + pub show_in_help: bool, + pub kind: UnstableFeatureKind, + pub env_var: Option<&'static str>, + pub config_option: ConfigFileOption, +} + +pub static FEATURE_DESCRIPTIONS: &[UnstableFeatureDescription] = &[ + UnstableFeatureDescription { + name: "bare-node-builtins", + help_text: "Enable unstable bare node builtins feature", + show_in_help: true, + kind: UnstableFeatureKind::Cli, + config_option: ConfigFileOption::SameAsFlagName, + env_var: Some("DENO_UNSTABLE_BARE_NODE_BUILTINS"), + }, + UnstableFeatureDescription { + name: "broadcast-channel", + help_text: "Enable unstable `BroadcastChannel` API", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "byonm", + help_text: "", + show_in_help: false, + kind: UnstableFeatureKind::Cli, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "cron", + help_text: "Enable unstable `Deno.cron` API", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "detect-cjs", + help_text: "Treats ambiguous .js, .jsx, .ts, .tsx files as CommonJS modules in more cases", + show_in_help: true, + kind: UnstableFeatureKind::Cli, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "ffi", + help_text: "Enable unstable FFI APIs", + show_in_help: false, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "fs", + help_text: "Enable unstable file system APIs", + show_in_help: false, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "http", + help_text: "Enable unstable HTTP APIs", + show_in_help: false, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "kv", + help_text: "Enable unstable KV APIs", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "lazy-dynamic-imports", + help_text: "Lazily loads statically analyzable dynamic imports when not running with type checking. Warning: This may change the order of semver specifier resolution.", + show_in_help: true, + kind: UnstableFeatureKind::Cli, + config_option: ConfigFileOption::SameAsFlagName, + env_var: Some("DENO_UNSTABLE_LAZY_DYNAMIC_IMPORTS"), + }, + UnstableFeatureDescription { + name: "lockfile-v5", + help_text: "Enable unstable lockfile v5", + show_in_help: true, + kind: UnstableFeatureKind::Cli, + config_option: ConfigFileOption::SameAsFlagName, + env_var: Some("DENO_UNSTABLE_LOCKFILE_V5"), + }, + UnstableFeatureDescription { + name: "net", + help_text: "enable unstable net APIs", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "no-legacy-abort", + help_text: "Enable abort signal in Deno.serve without legacy behavior. This will not abort the server when the request is handled successfully.", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "node-globals", + help_text: "Expose Node globals everywhere", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "npm-lazy-caching", + help_text: "Enable unstable lazy caching of npm dependencies, downloading them only as needed (disabled: all npm packages in package.json are installed on startup; enabled: only npm packages that are actually referenced in an import are installed", + show_in_help: true, + kind: UnstableFeatureKind::Cli, + config_option: ConfigFileOption::SameAsFlagName, + env_var: Some("DENO_UNSTABLE_NPM_LAZY_CACHING"), + }, + UnstableFeatureDescription { + name: "otel", + help_text: "Enable unstable OpenTelemetry features", + show_in_help: false, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "process", + help_text: "Enable unstable process APIs", + show_in_help: false, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "sloppy-imports", + help_text: "Enable unstable resolving of specifiers by extension probing, .js to .ts, and directory probing", + show_in_help: true, + kind: UnstableFeatureKind::Cli, + config_option: ConfigFileOption::SameAsFlagName, + env_var: Some("DENO_UNSTABLE_SLOPPY_IMPORTS"), + }, + UnstableFeatureDescription { + name: "temporal", + help_text: "Enable unstable Temporal API", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "unsafe-proto", + help_text: "Enable unsafe __proto__ support. This is a security risk.", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "vsock", + help_text: "Enable unstable VSOCK APIs", + show_in_help: false, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "webgpu", + help_text: "Enable unstable WebGPU APIs", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, + UnstableFeatureDescription { + name: "worker-options", + help_text: "Enable unstable Web Worker APIs", + show_in_help: true, + kind: UnstableFeatureKind::Runtime, + config_option: ConfigFileOption::SameAsFlagName, + env_var: None, + }, +]; diff --git a/runtime/features/gen.js b/runtime/features/gen.js new file mode 100644 index 0000000000..1b1e35f2df --- /dev/null +++ b/runtime/features/gen.js @@ -0,0 +1,26 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +/** + * Don't modify this file manually. + * + * This file is auto-generated by the build script, modify `data.rs` instead. + */ + +export const unstableIds = { + broadcastChannel: 1, + cron: 3, + ffi: 5, + fs: 6, + http: 7, + kv: 8, + net: 11, + noLegacyAbort: 12, + nodeGlobals: 13, + otel: 15, + process: 16, + temporal: 18, + unsafeProto: 19, + vsock: 20, + webgpu: 21, + workerOptions: 22, +}; diff --git a/runtime/features/gen.rs b/runtime/features/gen.rs new file mode 100644 index 0000000000..f4db68dff8 --- /dev/null +++ b/runtime/features/gen.rs @@ -0,0 +1,239 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +/// Don't modify this file manually. +/// +/// This file is auto-generated by the build script, modify `data.rs` instead. +use crate::structs::UnstableFeatureDefinition; +use crate::structs::UnstableFeatureKind; + +pub static UNSTABLE_FEATURES: &[UnstableFeatureDefinition] = &[ UnstableFeatureDefinition { + name: "bare-node-builtins", + flag_name: "unstable-bare-node-builtins", + help_text: "Enable unstable bare node builtins feature", + show_in_help: true, + id: 0, + kind: UnstableFeatureKind::Cli, + env_var: Some("DENO_UNSTABLE_BARE_NODE_BUILTINS"), + config_file_option: "bare-node-builtins", + }, + UnstableFeatureDefinition { + name: "broadcast-channel", + flag_name: "unstable-broadcast-channel", + help_text: "Enable unstable `BroadcastChannel` API", + show_in_help: true, + id: 1, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "broadcast-channel", + }, + UnstableFeatureDefinition { + name: "byonm", + flag_name: "unstable-byonm", + help_text: "", + show_in_help: false, + id: 2, + kind: UnstableFeatureKind::Cli, + env_var: None, + config_file_option: "byonm", + }, + UnstableFeatureDefinition { + name: "cron", + flag_name: "unstable-cron", + help_text: "Enable unstable `Deno.cron` API", + show_in_help: true, + id: 3, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "cron", + }, + UnstableFeatureDefinition { + name: "detect-cjs", + flag_name: "unstable-detect-cjs", + help_text: "Treats ambiguous .js, .jsx, .ts, .tsx files as CommonJS modules in more cases", + show_in_help: true, + id: 4, + kind: UnstableFeatureKind::Cli, + env_var: None, + config_file_option: "detect-cjs", + }, + UnstableFeatureDefinition { + name: "ffi", + flag_name: "unstable-ffi", + help_text: "Enable unstable FFI APIs", + show_in_help: false, + id: 5, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "ffi", + }, + UnstableFeatureDefinition { + name: "fs", + flag_name: "unstable-fs", + help_text: "Enable unstable file system APIs", + show_in_help: false, + id: 6, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "fs", + }, + UnstableFeatureDefinition { + name: "http", + flag_name: "unstable-http", + help_text: "Enable unstable HTTP APIs", + show_in_help: false, + id: 7, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "http", + }, + UnstableFeatureDefinition { + name: "kv", + flag_name: "unstable-kv", + help_text: "Enable unstable KV APIs", + show_in_help: true, + id: 8, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "kv", + }, + UnstableFeatureDefinition { + name: "lazy-dynamic-imports", + flag_name: "unstable-lazy-dynamic-imports", + help_text: "Lazily loads statically analyzable dynamic imports when not running with type checking. Warning: This may change the order of semver specifier resolution.", + show_in_help: true, + id: 9, + kind: UnstableFeatureKind::Cli, + env_var: Some("DENO_UNSTABLE_LAZY_DYNAMIC_IMPORTS"), + config_file_option: "lazy-dynamic-imports", + }, + UnstableFeatureDefinition { + name: "lockfile-v5", + flag_name: "unstable-lockfile-v5", + help_text: "Enable unstable lockfile v5", + show_in_help: true, + id: 10, + kind: UnstableFeatureKind::Cli, + env_var: Some("DENO_UNSTABLE_LOCKFILE_V5"), + config_file_option: "lockfile-v5", + }, + UnstableFeatureDefinition { + name: "net", + flag_name: "unstable-net", + help_text: "enable unstable net APIs", + show_in_help: true, + id: 11, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "net", + }, + UnstableFeatureDefinition { + name: "no-legacy-abort", + flag_name: "unstable-no-legacy-abort", + help_text: "Enable abort signal in Deno.serve without legacy behavior. This will not abort the server when the request is handled successfully.", + show_in_help: true, + id: 12, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "no-legacy-abort", + }, + UnstableFeatureDefinition { + name: "node-globals", + flag_name: "unstable-node-globals", + help_text: "Expose Node globals everywhere", + show_in_help: true, + id: 13, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "node-globals", + }, + UnstableFeatureDefinition { + name: "npm-lazy-caching", + flag_name: "unstable-npm-lazy-caching", + help_text: "Enable unstable lazy caching of npm dependencies, downloading them only as needed (disabled: all npm packages in package.json are installed on startup; enabled: only npm packages that are actually referenced in an import are installed", + show_in_help: true, + id: 14, + kind: UnstableFeatureKind::Cli, + env_var: Some("DENO_UNSTABLE_NPM_LAZY_CACHING"), + config_file_option: "npm-lazy-caching", + }, + UnstableFeatureDefinition { + name: "otel", + flag_name: "unstable-otel", + help_text: "Enable unstable OpenTelemetry features", + show_in_help: false, + id: 15, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "otel", + }, + UnstableFeatureDefinition { + name: "process", + flag_name: "unstable-process", + help_text: "Enable unstable process APIs", + show_in_help: false, + id: 16, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "process", + }, + UnstableFeatureDefinition { + name: "sloppy-imports", + flag_name: "unstable-sloppy-imports", + help_text: "Enable unstable resolving of specifiers by extension probing, .js to .ts, and directory probing", + show_in_help: true, + id: 17, + kind: UnstableFeatureKind::Cli, + env_var: Some("DENO_UNSTABLE_SLOPPY_IMPORTS"), + config_file_option: "sloppy-imports", + }, + UnstableFeatureDefinition { + name: "temporal", + flag_name: "unstable-temporal", + help_text: "Enable unstable Temporal API", + show_in_help: true, + id: 18, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "temporal", + }, + UnstableFeatureDefinition { + name: "unsafe-proto", + flag_name: "unstable-unsafe-proto", + help_text: "Enable unsafe __proto__ support. This is a security risk.", + show_in_help: true, + id: 19, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "unsafe-proto", + }, + UnstableFeatureDefinition { + name: "vsock", + flag_name: "unstable-vsock", + help_text: "Enable unstable VSOCK APIs", + show_in_help: false, + id: 20, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "vsock", + }, + UnstableFeatureDefinition { + name: "webgpu", + flag_name: "unstable-webgpu", + help_text: "Enable unstable WebGPU APIs", + show_in_help: true, + id: 21, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "webgpu", + }, + UnstableFeatureDefinition { + name: "worker-options", + flag_name: "unstable-worker-options", + help_text: "Enable unstable Web Worker APIs", + show_in_help: true, + id: 22, + kind: UnstableFeatureKind::Runtime, + env_var: None, + config_file_option: "worker-options", + }, +]; diff --git a/runtime/features/lib.rs b/runtime/features/lib.rs new file mode 100644 index 0000000000..047f3981fc --- /dev/null +++ b/runtime/features/lib.rs @@ -0,0 +1,10 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +mod gen; +mod structs; + +pub use gen::UNSTABLE_FEATURES; +pub use structs::UnstableFeatureKind; + +pub const JS_SOURCE: deno_core::FastStaticString = + deno_core::ascii_str_include!("./gen.js"); diff --git a/runtime/features/structs.rs b/runtime/features/structs.rs new file mode 100644 index 0000000000..63f924f3c5 --- /dev/null +++ b/runtime/features/structs.rs @@ -0,0 +1,20 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +#[derive(Clone, Debug)] +pub enum UnstableFeatureKind { + Cli, + Runtime, +} + +#[derive(Debug)] +#[allow(dead_code)] +pub struct UnstableFeatureDefinition { + pub name: &'static str, + pub flag_name: &'static str, + pub help_text: &'static str, + pub show_in_help: bool, + pub id: i32, + pub kind: UnstableFeatureKind, + pub env_var: Option<&'static str>, + pub config_file_option: &'static str, +} diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 28023a8819..d341656e90 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -31,6 +31,7 @@ import * as kv from "ext:deno_kv/01_db.ts"; import * as cron from "ext:deno_cron/01_cron.ts"; import * as webgpuSurface from "ext:deno_webgpu/02_surface.js"; import * as telemetry from "ext:deno_telemetry/telemetry.ts"; +import { unstableIds } from "ext:deno_features/flags.js"; import { loadWebGPU } from "ext:deno_webgpu/00_init.js"; const { ObjectDefineProperties } = primordials; @@ -150,26 +151,6 @@ const denoNs = { createHttpClient: httpClient.createHttpClient, }; -// NOTE(bartlomieju): keep IDs in sync with `runtime/lib.rs` -const unstableIds = { - broadcastChannel: 1, - cron: 2, - ffi: 3, - fs: 4, - http: 5, - kv: 6, - net: 7, - noLegacyAbort: 8, - nodeGlobals: 9, - otel: 10, - process: 11, - temporal: 12, - unsafeProto: 13, - vsock: 14, - webgpu: 15, - workerOptions: 16, -}; - const denoNsUnstableById = { __proto__: null }; // denoNsUnstableById[unstableIds.broadcastChannel] = { __proto__: null } diff --git a/runtime/lib.rs b/runtime/lib.rs index 1ce7da1907..9671dcaf57 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -51,133 +51,7 @@ pub use worker_bootstrap::WorkerExecutionMode; pub use worker_bootstrap::WorkerLogLevel; pub mod shared; +pub use deno_features::UnstableFeatureKind; +pub use deno_features::UNSTABLE_FEATURES; pub use deno_os::exit; pub use shared::runtime; - -pub struct UnstableGranularFlag { - pub name: &'static str, - pub help_text: &'static str, - pub show_in_help: bool, - // id to enable it in runtime/99_main.js - pub id: i32, -} - -// NOTE(bartlomieju): keep IDs in sync with `runtime/js/90_deno_ns.js` (search for `unstableFeatures`) -pub static UNSTABLE_GRANULAR_FLAGS: &[UnstableGranularFlag] = &[ - UnstableGranularFlag { - name: deno_broadcast_channel::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable `BroadcastChannel` API", - show_in_help: true, - id: 1, - }, - UnstableGranularFlag { - name: deno_cron::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable Deno.cron API", - show_in_help: true, - id: 2, - }, - UnstableGranularFlag { - name: deno_ffi::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable FFI APIs", - show_in_help: false, - id: 3, - }, - UnstableGranularFlag { - name: deno_fs::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable file system APIs", - show_in_help: false, - id: 4, - }, - UnstableGranularFlag { - name: ops::http::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable HTTP APIs", - show_in_help: false, - id: 5, - }, - UnstableGranularFlag { - name: deno_kv::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable Key-Value store APIs", - show_in_help: true, - id: 6, - }, - UnstableGranularFlag { - name: deno_net::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable net APIs", - show_in_help: true, - id: 7, - }, - UnstableGranularFlag { - name: "no-legacy-abort", - help_text: "Enable abort signal in Deno.serve without legacy behavior. This will not abort the server when the request is handled successfully.", - show_in_help: true, - id: 8, - }, - UnstableGranularFlag { - name: "node-globals", - help_text: "Expose Node globals everywhere", - show_in_help: true, - id: 9, - }, - UnstableGranularFlag { - name: "otel", - help_text: "Enable unstable OpenTelemetry features", - show_in_help: false, - id: 10, - }, - // TODO(bartlomieju): consider removing it - UnstableGranularFlag { - name: deno_process::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable process APIs", - show_in_help: false, - id: 11, - }, - UnstableGranularFlag { - name: "temporal", - help_text: "Enable unstable Temporal API", - show_in_help: true, - id: 12, - }, - UnstableGranularFlag { - name: "unsafe-proto", - help_text: "Enable unsafe __proto__ support. This is a security risk.", - show_in_help: true, - // This number is used directly in the JS code. Search - // for "unstableIds" to see where it's used. - id: 13, - }, - UnstableGranularFlag { - name: "vsock", - help_text: "Enable unstable VSOCK APIs", - show_in_help: false, - id: 14, - }, - UnstableGranularFlag { - name: deno_webgpu::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable `WebGPU` APIs", - show_in_help: true, - id: 15, - }, - UnstableGranularFlag { - name: ops::worker_host::UNSTABLE_FEATURE_NAME, - help_text: "Enable unstable Web Worker APIs", - show_in_help: true, - id: 16, - }, -]; - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn unstable_granular_flag_names_sorted() { - let flags = UNSTABLE_GRANULAR_FLAGS - .iter() - .map(|granular_flag| granular_flag.name.to_string()) - .collect::>(); - let mut sorted_flags = flags.clone(); - sorted_flags.sort(); - // sort the flags by name so they appear nicely in the help text - assert_eq!(flags, sorted_flags); - } -} diff --git a/runtime/ops/bootstrap.rs b/runtime/ops/bootstrap.rs index 5daa9f9736..8c2ec32020 100644 --- a/runtime/ops/bootstrap.rs +++ b/runtime/ops/bootstrap.rs @@ -96,8 +96,10 @@ pub fn op_bootstrap_user_agent(state: &mut OpState) -> String { pub fn op_bootstrap_unstable_args(state: &mut OpState) -> Vec { let options = state.borrow::(); let mut flags = Vec::with_capacity(options.unstable_features.len()); - for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() { - if options.unstable_features.contains(&granular_flag.id) { + for unstable_feature in &options.unstable_features { + if let Some(granular_flag) = + deno_features::UNSTABLE_FEATURES.get((*unstable_feature) as usize) + { flags.push(format!("--unstable-{}", granular_flag.name)); } } diff --git a/runtime/shared.rs b/runtime/shared.rs index f712cef65b..8947e57a16 100644 --- a/runtime/shared.rs +++ b/runtime/shared.rs @@ -46,6 +46,7 @@ extension!(runtime, { use deno_core::ascii_str_include; use deno_core::ExtensionFileSource; + ext.esm_files.to_mut().push(ExtensionFileSource::new("ext:deno_features/flags.js", deno_features::JS_SOURCE)); ext.esm_files.to_mut().push(ExtensionFileSource::new("ext:runtime_main/js/99_main.js", ascii_str_include!("./js/99_main.js"))); ext.esm_entry_point = Some("ext:runtime_main/js/99_main.js"); }