Skip to content

Commit 130bf37

Browse files
committed
feat: add Rust-based builtin modules generator with automated workflow
1 parent 683fb55 commit 130bf37

10 files changed

Lines changed: 194 additions & 2 deletions

File tree

.github/renovate.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
{
22
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
3-
"extends": ["github>Boshen/renovate"]
3+
"extends": ["github>Boshen/renovate"],
4+
"packageRules": [
5+
{
6+
"matchDatasources": ["node-version"],
7+
"matchDepNames": ["node"],
8+
"enabled": true
9+
}
10+
]
411
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Update Built-in Modules
2+
3+
permissions:
4+
contents: write
5+
pull-requests: write
6+
7+
on:
8+
push:
9+
branches:
10+
- main
11+
paths:
12+
- ".node-version"
13+
workflow_dispatch:
14+
15+
defaults:
16+
run:
17+
shell: bash
18+
19+
jobs:
20+
update-builtins:
21+
name: Update Node.js Built-in Modules
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
25+
with:
26+
token: ${{ secrets.OXC_BOT_PAT }}
27+
28+
- name: Read Node.js version
29+
id: node-version
30+
run: echo "version=$(cat .node-version)" >> $GITHUB_OUTPUT
31+
32+
- name: Setup Node.js
33+
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
34+
with:
35+
node-version: ${{ steps.node-version.outputs.version }}
36+
37+
- name: Setup Rust
38+
uses: oxc-project/setup-rust@ecabb7322a2ba5aeedb3612d2a40b86a85cee235 # v1.0.11
39+
40+
- name: Update built-in modules list
41+
run: cargo run -p update_builtins
42+
43+
- name: Format generated code
44+
run: cargo fmt
45+
46+
- name: Run tests
47+
run: cargo test
48+
49+
- uses: stefanzweifel/git-auto-commit-action@28e16e81777b558cc906c8750092100bbb34c5e3 # v7.0.0
50+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
51+
with:
52+
commit_message: "chore: update built-in modules for Node.js ${{ steps.node-version.outputs.version }} [skip ci]"
53+
file_pattern: src/lib.rs

.node-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
24.0.0

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
[workspace]
2+
members = [".", "tasks/update_builtins"]
3+
resolver = "2"
4+
15
[package]
26
name = "nodejs-built-in-modules"
37
version = "0.0.1"

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Found 12 outliers among 100 measurements (12.00%)
2626
**Result:** The `contains` method is ~18% faster than `binary_search` (3.24 µs vs 3.84 µs).
2727

2828
Run benchmarks with:
29+
2930
```bash
3031
cargo bench
3132
```

benches/is_builtin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use criterion::{black_box, criterion_group, criterion_main, Criterion};
1+
use criterion::{Criterion, black_box, criterion_group, criterion_main};
22
use nodejs_built_in_modules::{BUILTINS, BUILTINS_WITH_MANDATORY_NODE_PREFIX};
33

44
/// Version using contains (current implementation)

justfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ alias r := ready
1111
init:
1212
cargo binstall watchexec-cli cargo-insta typos-cli cargo-shear dprint -y
1313

14+
update-builtins:
15+
cargo run -p update_builtins
16+
cargo fmt
17+
1418
ready:
1519
git diff --exit-code --quiet
1620
typos

tasks/update_builtins/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "update_builtins"
3+
version = "0.0.0"
4+
edition = "2024"
5+
publish = false
6+
7+
[[bin]]
8+
name = "update_builtins"
9+
path = "src/main.rs"
10+
11+
[dependencies]
12+
quote = "1.0"
13+
serde_json = "1.0"

tasks/update_builtins/src/main.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use quote::quote;
2+
use std::{
3+
fs,
4+
process::{Command, Stdio},
5+
};
6+
7+
fn main() {
8+
let node_version_path = ".node-version";
9+
let node_version = fs::read_to_string(node_version_path)
10+
.expect("Failed to read .node-version")
11+
.trim()
12+
.to_string();
13+
14+
// Get Node.js major version
15+
let node_major_version = node_version.split('.').next().unwrap();
16+
17+
println!("Using Node.js version: {node_version}");
18+
19+
// Run Node.js command to get built-in modules
20+
let output = Command::new("node")
21+
.arg("-p")
22+
.arg("[...require('module').builtinModules].map(b => JSON.stringify(b)).join(',\\n')")
23+
.stdout(Stdio::piped())
24+
.stderr(Stdio::piped())
25+
.output()
26+
.expect("Failed to execute node command");
27+
28+
if !output.status.success() {
29+
eprintln!("Node.js command failed:");
30+
eprintln!("{}", String::from_utf8_lossy(&output.stderr));
31+
std::process::exit(1);
32+
}
33+
34+
let modules_output = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
35+
36+
// Parse the module names (they're JSON strings separated by commas and newlines)
37+
let json_array_str = format!("[{}]", modules_output.trim());
38+
let all_modules: Vec<String> =
39+
serde_json::from_str(&json_array_str).expect("Failed to parse module list");
40+
41+
// Separate modules with mandatory node: prefix from regular modules
42+
let mut mandatory_prefix_modules: Vec<String> = all_modules
43+
.iter()
44+
.filter(|m| m.starts_with("node:"))
45+
.map(|m| m.strip_prefix("node:").unwrap().to_string())
46+
.collect();
47+
mandatory_prefix_modules.sort();
48+
49+
let mut regular_modules: Vec<String> = all_modules
50+
.iter()
51+
.filter(|m| !m.starts_with("node:"))
52+
.cloned()
53+
.collect();
54+
regular_modules.sort();
55+
56+
// Build the complete file with doc comments using quote!
57+
let regular_module_refs = &regular_modules;
58+
let mandatory_module_refs = &mandatory_prefix_modules;
59+
60+
let content = format!(
61+
r#"//! Node.js v{} built-in modules
62+
//!
63+
//! <https://nodejs.org/api/modules.html#built-in-modules>
64+
65+
/// Generated by
66+
///
67+
/// `node -p "[...require('module').builtinModules].map(b => JSON.stringify(b)).join(',\n')"`
68+
///
69+
/// with `node:` prefixed values moved [BUILTINS_WITH_MANDATORY_NODE_PREFIX].
70+
{}
71+
72+
/// <https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix>
73+
{}
74+
75+
/// Is `specifier` a node.js built-in module?
76+
{}
77+
"#,
78+
node_major_version,
79+
quote! { pub static BUILTINS: &[&str] = &[#(#regular_module_refs,)*]; },
80+
quote! { pub static BUILTINS_WITH_MANDATORY_NODE_PREFIX: &[&str] = &[#(#mandatory_module_refs),*]; },
81+
quote! {
82+
pub fn is_nodejs_builtin_module(specifier: &str) -> bool {
83+
if let Some(stripped) = specifier.strip_prefix("node:") {
84+
return BUILTINS.contains(&stripped)
85+
|| BUILTINS_WITH_MANDATORY_NODE_PREFIX.contains(&stripped);
86+
}
87+
BUILTINS.contains(&specifier)
88+
}
89+
}
90+
);
91+
92+
// Write to src/lib.rs
93+
fs::write("src/lib.rs", content).expect("Failed to write src/lib.rs");
94+
95+
println!("✓ Updated built-in modules for Node.js v{node_version}");
96+
println!(" - {} regular modules", regular_modules.len());
97+
println!(
98+
" - {} mandatory prefix modules",
99+
mandatory_prefix_modules.len()
100+
);
101+
}

0 commit comments

Comments
 (0)