Skip to content

Commit ba95fb9

Browse files
fcouryclaude
andcommitted
feat: implement complete js_name attribute support with import aliasing
This commit completes the js_name attribute implementation by fixing the critical import generation bug and implementing the recommended aliasing approach for extern function imports. Key changes: - Fixed broken import generation that tried to import non-existent snake_case names - Implemented import aliasing: `import { existsSync as exists_sync } from 'fs'` - Updated semantic analyzer to pass extern_functions mappings to transpiler - Enhanced transpiler import logic to check js_name mappings during generation - Updated fs-example to use snake_case function names consistently The aliasing approach allows Husk developers to use familiar snake_case conventions while properly importing JavaScript camelCase function names, providing the best developer experience and language consistency. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 82f4479 commit ba95fb9

File tree

3 files changed

+46
-21
lines changed

3 files changed

+46
-21
lines changed

examples/fs-example/src/main.husk

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use self::fs_types;
33

44
// Import the functions we need from fs
55
use fs::{
6-
existsSync, readFileSync, writeFileSync, statSync, mkdirSync, readdirSync,
7-
renameSync, copyFileSync, accessSync, unlinkSync, rmdirSync
6+
exists_sync, read_file_sync, write_file_sync, stat_sync, mkdir_sync, readdir_sync,
7+
rename_sync, copy_file_sync, access_sync, unlink_sync, rmdir_sync
88
};
99

1010
fn main() {
@@ -14,30 +14,30 @@ fn main() {
1414
// Example 1: Check if a file exists
1515
let test_file = "test.txt";
1616
println!("1. Checking if {} exists...", test_file);
17-
if existsSync(test_file) {
17+
if exists_sync(test_file) {
1818
println!(" ✓ File exists!");
1919

2020
// Read the existing file
21-
let content = readFileSync(test_file, "utf8");
21+
let content = read_file_sync(test_file, "utf8");
2222
println!(" Current content: {}", content);
2323
} else {
2424
println!(" ✗ File does not exist, creating it...");
2525

2626
// Create the file with some content
27-
writeFileSync(test_file, "Hello from Husk!\nThis file was created using Node.js fs module.");
27+
write_file_sync(test_file, "Hello from Husk!\nThis file was created using Node.js fs module.");
2828
println!(" ✓ File created!");
2929
}
3030

3131
// Example 2: Get file stats
3232
println!("\n2. Getting file stats for {}...", test_file);
33-
let stats = statSync(test_file);
33+
let stats = stat_sync(test_file);
3434
println!(" ✓ Stats retrieved (object created)");
3535

3636
// Example 3: Create a directory
3737
let test_dir = "test-dir";
3838
println!("\n3. Creating directory: {}", test_dir);
39-
if !existsSync(test_dir) {
40-
mkdirSync(test_dir);
39+
if !exists_sync(test_dir) {
40+
mkdir_sync(test_dir);
4141
println!(" ✓ Directory created!");
4242
} else {
4343
println!(" → Directory already exists");
@@ -47,46 +47,46 @@ fn main() {
4747
let json_file = format!("{}/data.json", test_dir);
4848
println!("\n4. Writing JSON file: {}", json_file);
4949
let json_content = "{\n \"message\": \"Hello from Husk\",\n \"version\": \"1.0.0\"\n}";
50-
writeFileSync(json_file, json_content);
50+
write_file_sync(json_file, json_content);
5151
println!(" ✓ JSON file written!");
5252

5353
// Example 5: List directory contents
5454
println!("\n5. Listing contents of current directory...");
55-
let files = readdirSync(".");
55+
let files = readdir_sync(".");
5656
println!(" ✓ Directory read successfully");
5757

5858
// Example 6: Read the JSON file back
5959
println!("\n6. Reading JSON file back...");
60-
let json_data = readFileSync(json_file, "utf8");
60+
let json_data = read_file_sync(json_file, "utf8");
6161
println!(" Content:\n{}", json_data);
6262

6363
// Example 7: Rename a file
6464
let new_json_file = format!("{}/renamed-data.json", test_dir);
6565
println!("\n7. Renaming {} to {}...", json_file, new_json_file);
66-
renameSync(json_file, new_json_file);
66+
rename_sync(json_file, new_json_file);
6767
println!(" ✓ File renamed!");
6868

6969
// Example 8: Copy a file
7070
let copied_file = format!("{}/copied-test.txt", test_dir);
7171
println!("\n8. Copying {} to {}...", test_file, copied_file);
72-
copyFileSync(test_file, copied_file);
72+
copy_file_sync(test_file, copied_file);
7373
println!(" ✓ File copied!");
7474

7575
// Example 9: Check file access
7676
println!("\n9. Checking file access permissions...");
7777
// Note: fs.constants.F_OK is typically 0
78-
accessSync(test_file, 0); // Check if file exists and is visible
78+
access_sync(test_file, 0); // Check if file exists and is visible
7979
println!(" ✓ File is accessible!");
8080

8181
// Example 10: Clean up - remove test files
8282
println!("\n10. Cleaning up test files...");
83-
unlinkSync(new_json_file);
83+
unlink_sync(new_json_file);
8484
println!(" ✓ Removed {}", new_json_file);
85-
unlinkSync(copied_file);
85+
unlink_sync(copied_file);
8686
println!(" ✓ Removed {}", copied_file);
87-
rmdirSync(test_dir);
87+
rmdir_sync(test_dir);
8888
println!(" ✓ Removed {}", test_dir);
89-
unlinkSync(test_file);
89+
unlink_sync(test_file);
9090
println!(" ✓ Removed {}", test_file);
9191

9292
println!("\n✅ All examples completed successfully!");

src/semantic.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,11 @@ impl SemanticVisitor {
707707
);
708708
}
709709
}
710+
711+
// Merge extern functions with js_name mappings
712+
for (name, js_name) in &module_analyzer.extern_functions {
713+
self.extern_functions.insert(name.clone(), js_name.clone());
714+
}
710715
}
711716

712717
fn process_extern_item(&mut self, item: &ExternItem, prefix: &str) -> Result<()> {

src/transpiler.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2930,10 +2930,30 @@ impl JsTranspiler {
29302930
let imports = items
29312931
.iter()
29322932
.map(|(name, alias)| {
2933-
if let Some(alias) = alias {
2934-
format!("{name} as {alias}")
2933+
// Check if this is an import from an extern module with js_name
2934+
if path.prefix == UsePrefix::None && !path.segments.is_empty() {
2935+
// This is an external module import - check for js_name mapping
2936+
let module_name = &path.segments[0];
2937+
let full_name = format!("{}::{}", module_name, name);
2938+
// Check if this function has a js_name mapping
2939+
if let Some(js_name) = self.extern_functions.get(&full_name) {
2940+
// Import the JavaScript name and alias it to the Husk name
2941+
format!("{js_name} as {name}")
2942+
} else {
2943+
// No js_name mapping, import as-is
2944+
if let Some(alias) = alias {
2945+
format!("{name} as {alias}")
2946+
} else {
2947+
name.clone()
2948+
}
2949+
}
29352950
} else {
2936-
name.clone()
2951+
// Local import, handle aliases normally
2952+
if let Some(alias) = alias {
2953+
format!("{name} as {alias}")
2954+
} else {
2955+
name.clone()
2956+
}
29372957
}
29382958
})
29392959
.collect::<Vec<_>>()

0 commit comments

Comments
 (0)