Skip to content

Bug: WASM file lookup fails for crate names containing hyphens #1

@suprabhatrapolu

Description

@suprabhatrapolu

Summary

dwasm fails at step 2 (wasm-bindgen) for any crate whose name contains hyphens (e.g. dsprint-survey, enterprise-portal). The build succeeds but the WASM file is never found because dwasm looks for the wrong filename.

Root Cause

Line 70 in src/main.rs:

let wasm_name = cli.crate_name.replace('-', "_");

This converts dsprint-surveydsprint_survey, then on line 92 constructs the WASM path as:

let wasm_file = project
    .join("target/wasm32-unknown-unknown/release")
    .join(format!("{}.wasm", wasm_name));
// Looks for: target/.../release/dsprint_survey.wasm

But cargo build outputs the WASM file using the original crate name with hyphens intact:

target/wasm32-unknown-unknown/release/dsprint-survey.wasm

The hyphen-to-underscore conversion is correct for Rust use statements and for wasm-bindgen output filenames (the _bg.wasm and .js files DO use underscores), but it's wrong for the initial cargo build output .wasm file, which keeps the original Cargo.toml package name.

Reproduction

# Succeeds (no hyphens):
dwasm --crate-name dinkedin --project /opt/dinkedin --standalone
# ✓ Finds: target/.../release/dinkedin.wasm

# Fails (has hyphens):
dwasm --crate-name dsprint-survey --project /opt/dsprint-survey --standalone
# ✗ Looks for: target/.../release/dsprint_survey.wasm
# ✗ Actual file: target/.../release/dsprint-survey.wasm

dwasm --crate-name enterprise-portal --project /opt/enterprise-portal --standalone
# ✗ Same issue

Error output:

[1/5] cargo build --release --target wasm32-unknown-unknown
    Finished `release` profile [optimized] target(s) in 23.32s
[2/5] wasm-bindgen
WASM file not found: /opt/dsprint-survey/target/wasm32-unknown-unknown/release/dsprint_survey.wasm

Proof the file exists with hyphens:

$ ls /opt/dsprint-survey/target/wasm32-unknown-unknown/release/*.wasm
/opt/dsprint-survey/target/wasm32-unknown-unknown/release/dsprint-survey.wasm

Affected Crates

Any DIRMACS project with a hyphenated crate name:

  • dsprint-survey — currently built with trunk as workaround
  • enterprise-portal — currently built with trunk as workaround

Projects without hyphens work fine:

  • dinkedin, dirmacs-admin (uses underscore internally), ehb-admin, ehb-buddy, eruka-web

Wait — dirmacs-admin and ehb-admin also have hyphens but they work. Let me clarify: cargo uses the crate name from Cargo.toml [package] name for the output binary. The issue is specifically about how cargo names the .wasm output vs how wasm-bindgen names its output. Cargo keeps hyphens in the .wasm filename; wasm-bindgen converts them to underscores in _bg.wasm and .js.

Update after checking: Actually dirmacs-admin and ehb-admin DO have hyphens and DO work with dwasm. So the behavior might depend on the Rust/cargo version or some other factor. The key point is: the .wasm file in target/ uses the original name with hyphens, and dwasm should look for that first.

Proposed Fix

The fix is to try BOTH the hyphenated and underscored filenames when looking for the cargo output. Change line 90-96:

// Step 2: wasm-bindgen
step("2/5", "wasm-bindgen");
let wasm_file = {
    let underscore_name = project
        .join("target/wasm32-unknown-unknown/release")
        .join(format!("{}.wasm", wasm_name)); // underscored
    let hyphen_name = project
        .join("target/wasm32-unknown-unknown/release")
        .join(format!("{}.wasm", cli.crate_name)); // original with hyphens
    
    if underscore_name.exists() {
        underscore_name
    } else if hyphen_name.exists() {
        hyphen_name
    } else {
        eprintln!("WASM file not found. Checked:");
        eprintln!("  {}", underscore_name.display());
        eprintln!("  {}", hyphen_name.display());
        std::process::exit(1);
    }
};

This is backwards-compatible — existing working builds still find the underscore version first, and hyphenated crate names now fall through to the hyphen version.

Alternative (more robust): Query cargo for the actual output path

Instead of guessing the filename, use cargo metadata or parse the cargo build output (--message-format=json) to get the exact artifact path:

let output = Command::new("cargo")
    .args(["build", "--release", "--target", "wasm32-unknown-unknown",
           "--message-format=json", "-p", &cli.crate_name])
    .current_dir(&project)
    .output()
    .expect("cargo build");

// Parse JSON lines to find the artifact path
let wasm_file = String::from_utf8_lossy(&output.stdout)
    .lines()
    .filter_map(|line| serde_json::from_str::<serde_json::Value>(line).ok())
    .find(|msg| msg["reason"] == "compiler-artifact" 
          && msg["target"]["name"].as_str() == Some(&cli.crate_name))
    .and_then(|msg| msg["filenames"].as_array()?.iter()
        .find(|f| f.as_str()?.ends_with(".wasm"))
        .and_then(|f| f.as_str().map(PathBuf::from)))
    .unwrap_or_else(|| {
        eprintln!("Could not determine WASM output path from cargo");
        std::process::exit(1);
    });

This approach is more complex but eliminates the filename guessing entirely.

Recommended Approach

Go with the simple fix (try both filenames). It's a 10-line change, zero new dependencies, backwards-compatible, and handles all known cases. The cargo --message-format=json approach is better long-term but adds serde_json as a dependency.

Environment

  • dwasm v0.1.1
  • Rust 1.86 (nightly)
  • wasm-bindgen 0.2.114
  • Trunk 0.21 (wasm-opt v123)
  • VPS: Ubuntu 24.04

Labels

bug, good-first-issue

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinggood first issueGood for newcomershelp wantedExtra attention is neededquestionFurther information is requested

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions