diff --git a/crates/wasm/Cargo.toml b/crates/wasm/Cargo.toml index 62535e0f61..981293cd68 100644 --- a/crates/wasm/Cargo.toml +++ b/crates/wasm/Cargo.toml @@ -49,4 +49,7 @@ web-sys = { version = "0.3.64", features = ["console"] } [dev-dependencies] wasm-bindgen-test = "0.3.37" -serde_json = "1.0.107" \ No newline at end of file +serde_json = "1.0.107" + +[profile.release] +lto = true \ No newline at end of file diff --git a/crates/wasm/publish/README.md b/crates/wasm/publish/README.md index 46c47db529..bc5af01d14 100644 --- a/crates/wasm/publish/README.md +++ b/crates/wasm/publish/README.md @@ -7,7 +7,7 @@ use Rust functionality via .wasm. # Install wasm-pack first: https://rustwasm.github.io/wasm-pack/installer/ npm install -npm run publish-wasm +npm run publish-wasm-bundle ``` We have a release github action that runs these on every release. diff --git a/crates/wasm/publish/package.json b/crates/wasm/publish/package.json index 1d1007d39f..e37412b399 100644 --- a/crates/wasm/publish/package.json +++ b/crates/wasm/publish/package.json @@ -6,15 +6,16 @@ "scripts": { "format": "prettier --write .", "compile-wasm": "tsc && node build/run.js", - "publish-wasm": "npm run compile-wasm -- --publish" + "publish-wasm": "npm run compile-wasm -- --publish", + "publish-wasm-bundle": "npm run compile-wasm -- --publish --bundle" }, "dependencies": { "wasm-pack": "^0.12.1" }, "devDependencies": { - "@types/node": "^20.5.6", + "@types/node": "^20.9.4", + "prettier": "^3.0.2", "tsx": "^3.12.7", - "typescript": "^5.2.2", - "prettier": "^3.0.2" + "typescript": "^5.2.2" } } diff --git a/crates/wasm/publish/run.ts b/crates/wasm/publish/run.ts index 5d69bbb812..fc028a45f0 100644 --- a/crates/wasm/publish/run.ts +++ b/crates/wasm/publish/run.ts @@ -1,10 +1,14 @@ import path from 'path'; import { execSync } from 'child_process'; -import { readFileSync, writeFileSync } from 'fs'; +import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; -const TARGETS = ['web', 'nodejs', 'bundler']; +const TARGETS_WITHOUT_BIN = ['web', 'nodejs', 'bundler']; +const TARGETS_WITH_BIN = ['web-bin', 'nodejs-bin', 'bundler-bin']; -TARGETS.forEach(target => { +TARGETS_WITHOUT_BIN.forEach(wasmCompile); +TARGETS_WITH_BIN.forEach(wasmCompileBinary); + +function wasmCompile(target: string): void { // Run wasm-pack for each target execSync( `wasm-pack build ../ --release --target ${target} --out-name index --out-dir publish/${target}`, @@ -16,7 +20,8 @@ TARGETS.forEach(target => { // Rename package to target-specific names const packageJsonPath = path.join(process.cwd(), `${target}/package.json`); const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); - packageJson.name = `@penumbra-zone/wasm-${target}`; + + packageJson.name = `@penumbra-zone-test/wasm-${target}`; writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8'); // Without packing first, the .wasm's will not be included @@ -30,4 +35,76 @@ TARGETS.forEach(target => { // Change working directory back to parent process.chdir('..'); -}); +}; + +function wasmCompileBinary(target: string): void { + // Execute if 'bundle' flag is set + if (process.argv.includes('--bundle')) { + // Logically seperate the copy and build targets + const buildTarget = target.replace('-bin', ''); + const copyTarget = target; + + // Run wasm-pack for each target + execSync( + `wasm-pack build ../ --release --target ${buildTarget} --out-name index --out-dir publish/${copyTarget}`, + { + stdio: 'inherit', + }, + ); + // Copy binary files to the package directory + const binaryDir = path.join(process.cwd(), '../../crypto/proof-params/src/gen/'); + const targetPackageDir = path.join(process.cwd(), `${copyTarget}`); + + // Ensure the target directory exists + if (existsSync(binaryDir)) { + const targetBinaryDir = path.join(targetPackageDir, 'bin'); + if (!existsSync(targetBinaryDir)) { + mkdirSync(targetBinaryDir); + } + + // Copy binary files to the package directory + const binaryFiles = [ + 'delegator_vote_pk.bin', + 'nullifier_derivation_pk.bin', + 'output_pk.bin', + 'spend_pk.bin', + 'swap_pk.bin', + 'swapclaim_pk.bin', + 'undelegateclaim_pk.bin' + ]; + binaryFiles.forEach(file => { + const sourcePath = path.join(binaryDir, file); + const targetPath = path.join(targetBinaryDir, file); + copyFileSync(sourcePath, targetPath); + }); + } else { + // Throw error if target directory doesn't exist + throw new Error(`The directory ${binaryDir} does not exist.`); + } + + // Rename package to target-specific names + const packageJsonPath = path.join(process.cwd(), `${copyTarget}/package.json`); + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); + + // Check if the 'files' property in the generated package.json includes the + // bin directory, otherwise include it. Without this line, wasm-pack will + // fail to bundle the binary proving keys inside the NPM package. + if (!packageJson.files.includes('bin')) { + packageJson.files.push('bin'); + } + packageJson.name = `@penumbra-zone-test/wasm-${copyTarget}`; + writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8'); + + // Without packing first, the .wasm's will not be included + process.chdir(copyTarget); + execSync('npm pack', { stdio: 'inherit' }); + + // Publish to npm if flag provided + if (process.argv.includes('--publish')) { + execSync('npm publish --access public', { stdio: 'inherit' }); + } + + // Change working directory back to parent + process.chdir('..'); + } +};