deno_features 0.9.0

Provides definitions of Deno unstable features.
Documentation
// Copyright 2018-2025 the Deno authors. MIT license.

#![allow(clippy::disallowed_methods)]

use std::path::Path;

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] = &[\n",
  );

  let mut descriptions = data::FEATURE_DESCRIPTIONS.to_vec();
  descriptions.sort_by_key(|desc| desc.name);

  for (id, feature) in descriptions.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",
    };

    rs_list += &format!(
      r#"  UnstableFeatureDefinition {{
    name: "{}",
    flag_name: "{}",
    help_text: "{}",
    show_in_help: {},
    id: {},
    kind: {},
    config_file_option: "{}",
  }},
"#,
      feature.name,
      flag_name,
      feature.help_text,
      feature.show_in_help,
      id,
      feature_kind,
      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";

  let mut env_var_def = "pub struct UnstableEnvVarNames {\n".to_string();
  let mut env_var_impl =
    "pub static UNSTABLE_ENV_VAR_NAMES: UnstableEnvVarNames = UnstableEnvVarNames {\n"
      .to_string();
  for feature in &descriptions {
    let value = match feature.env_var {
      Some(v) => v,
      None => continue,
    };
    let prop_name = feature.name.replace("-", "_");
    env_var_def.push_str(&format!("  pub {}: &'static str,\n", prop_name));
    env_var_impl.push_str(&format!("  {}: \"{}\",\n", prop_name, value));
  }
  env_var_def.push_str("}\n");
  env_var_impl.push_str("};\n");

  rs_list.push_str(&env_var_def);
  rs_list.push_str(&env_var_impl);

  write_if_changed(&crate_dir.join("gen.js"), &js_list);
  write_if_changed(&crate_dir.join("gen.rs"), &rs_list);
}

fn write_if_changed(path: &Path, new_text: &str) {
  let current_text = std::fs::read_to_string(path).unwrap_or_default();
  if current_text != new_text {
    std::fs::write(path, new_text).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
}