diff --git a/LICENSE b/LICENSE index edf73ca..2429503 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2024 the Deno authors +Copyright (c) 2018-2025 the Deno authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index d712dc4..d24411f 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,14 @@ Add a task to the _deno.json_ file in your project: } ``` +## Browser, Node.js, or older Deno support + +The output is compatible in Deno 2.1+ or Deno 2.1.5+ if published to JSR (due to +a bug unfortunately). + +Most browsers do not support Wasm imports yet, which this library generates. If +you want output that works in more scenarios, build with the `--inline` flag. + ## Scaffold project (Optional) To create a starter Rust crate in an `rs_lib` subfolder of your project, run: @@ -42,9 +50,9 @@ console.log(add(1, 1)); ## Checking output is up-to-date -It may occur that someone updates the Rust code, but forgets to build when -submitting a PR. To ensure that the output is up-to-date, you can use the -`--check` flag: +If you're checking your Wasm module into source control, it may occur that +someone updates the Rust code, but forgets to build when submitting a PR. To +ensure that the output is up-to-date, you can use the `--check` flag: ```shellsession deno task wasmbuild --check @@ -60,6 +68,8 @@ For example, in a GitHub action: ### CLI flags - `--debug` - Build without optimizations. +- `--inline` - Inline the Wasm module. Useful for scenarios where you want a + build output that works in environments that don't support Wasm imports yet. - `--project ` / `-p ` - Specifies the crate to build when using a Cargo workspace. - `--out ` - Specifies the output directory. Defaults to `./lib` diff --git a/deno.json b/deno.json index 34d3887..65ede94 100644 --- a/deno.json +++ b/deno.json @@ -22,21 +22,22 @@ "build": "deno run -A ./main.ts -p wasmbuild", "build:bindgen-upgrade": "WASMBUILD_BINDGEN_UPGRADE=1 deno task build", "build:lkg": "deno run -A jsr:@deno/wasmbuild@^0.15.4 -p wasmbuild", - "test": "cd tests && rm -rf lib lib_out_js_file lib_sync lib_no_cache && deno task test:main && deno task test:js-ext && deno test -A && deno task test:check", + "test": "cd tests && rm -rf lib lib_out_js_file lib_inline lib_no_cache && deno task test:main && deno task test:js-ext && deno task test:inline && deno test -A && deno task test:check", "test:main": "cd tests && deno run -A ../main.ts -p deno_test", "test:js-ext": "deno task test:main --js-ext mjs --out lib_out_js_file && cat tests/lib_out_js_file/deno_test.mjs > /dev/null", - "test:check": "deno task test:main --check" + "test:check": "deno task test:main --check", + "test:inline": "deno task test:main --inline --out lib_inline" }, "imports": { - "@david/dax": "jsr:@david/dax@^0.39.2", + "@david/path": "jsr:@david/path@^0.2.0", "@david/temp": "jsr:@david/temp@^0.1.1", - "@std/assert": "jsr:@std/assert@^0.218.2", - "@std/cli": "jsr:@std/cli@^0.218.2", - "@std/encoding": "jsr:@std/encoding@^0.218.2", - "@std/fmt": "jsr:@std/fmt@^0.218.2", - "@std/fs": "jsr:@std/fs@^0.218.2", - "@std/path": "jsr:@std/path@^0.218.2", - "@std/streams": "jsr:@std/streams@^1.0.6", - "@std/tar": "jsr:@std/tar@^0.1.2" + "@std/assert": "jsr:@std/assert@^1.0.11", + "@std/cli": "jsr:@std/cli@^1.0.11", + "@std/encoding": "jsr:@std/encoding@^1.0.6", + "@std/fmt": "jsr:@std/fmt@^1.0.4", + "@std/fs": "jsr:@std/fs@^1.0.10", + "@std/path": "jsr:@std/path@^1.0.8", + "@std/streams": "jsr:@std/streams@^1.0.8", + "@std/tar": "jsr:@std/tar@^0.1.4" } } diff --git a/deno.lock b/deno.lock index a54c960..db8eefc 100644 --- a/deno.lock +++ b/deno.lock @@ -1,45 +1,22 @@ { "version": "4", "specifiers": { - "jsr:@david/dax@~0.39.2": "0.39.2", "jsr:@david/path@0.2": "0.2.0", "jsr:@david/temp@~0.1.1": "0.1.1", - "jsr:@david/which@0.3": "0.3.0", - "jsr:@std/assert@0.213": "0.213.1", - "jsr:@std/assert@~0.218.2": "0.218.2", - "jsr:@std/bytes@0.213": "0.213.1", - "jsr:@std/bytes@^1.0.2": "1.0.2", - "jsr:@std/cli@~0.218.2": "0.218.2", - "jsr:@std/encoding@~0.218.2": "0.218.2", - "jsr:@std/fmt@0.213.0": "0.213.0", - "jsr:@std/fmt@~0.218.2": "0.218.2", - "jsr:@std/fs@0.213.0": "0.213.0", - "jsr:@std/fs@1": "1.0.8", - "jsr:@std/fs@~0.218.2": "0.218.2", - "jsr:@std/io@0.213": "0.213.0", - "jsr:@std/io@0.213.0": "0.213.0", - "jsr:@std/path@0.213": "0.213.0", - "jsr:@std/path@0.213.0": "0.213.0", + "jsr:@std/assert@^1.0.11": "1.0.11", + "jsr:@std/bytes@^1.0.3": "1.0.4", + "jsr:@std/cli@^1.0.11": "1.0.11", + "jsr:@std/encoding@^1.0.6": "1.0.6", + "jsr:@std/fmt@^1.0.4": "1.0.4", + "jsr:@std/fs@1": "1.0.10", + "jsr:@std/fs@^1.0.10": "1.0.10", + "jsr:@std/internal@^1.0.5": "1.0.5", "jsr:@std/path@1": "1.0.8", "jsr:@std/path@^1.0.8": "1.0.8", - "jsr:@std/path@~0.218.2": "0.218.2", - "jsr:@std/streams@0.213.0": "0.213.0", - "jsr:@std/streams@^1.0.6": "1.0.7", - "jsr:@std/streams@^1.0.7": "1.0.7", - "jsr:@std/tar@~0.1.2": "0.1.2" + "jsr:@std/streams@^1.0.8": "1.0.8", + "jsr:@std/tar@~0.1.4": "0.1.4" }, "jsr": { - "@david/dax@0.39.2": { - "integrity": "f7fde219e9a4b331024bcc89bfc5c63b85348cad61ff26a963d893262d92b01c", - "dependencies": [ - "jsr:@david/which", - "jsr:@std/fmt@0.213.0", - "jsr:@std/fs@0.213.0", - "jsr:@std/io@0.213.0", - "jsr:@std/path@0.213.0", - "jsr:@std/streams@0.213.0" - ] - }, "@david/path@0.2.0": { "integrity": "f2d7aa7f02ce5a55e27c09f9f1381794acb09d328f8d3c8a2e3ab3ffc294dccd", "dependencies": [ @@ -53,121 +30,61 @@ "jsr:@david/path" ] }, - "@david/which@0.3.0": { - "integrity": "6bdb62c40ac90edcf328e854fa8103a8db21e7c326089cbe3c3a1cf7887d3204" - }, - "@std/assert@0.213.1": { - "integrity": "24c28178b30c8e0782c18e8e94ea72b16282207569cdd10ffb9d1d26f2edebfe" - }, - "@std/assert@0.218.2": { - "integrity": "7f0a5a1a8cf86607cd6c2c030584096e1ffad27fc9271429a8cb48cfbdee5eaf", + "@std/assert@1.0.11": { + "integrity": "2461ef3c368fe88bc60e186e7744a93112f16fd110022e113a0849e94d1c83c1", "dependencies": [ - "jsr:@std/fmt@~0.218.2" + "jsr:@std/internal" ] }, - "@std/bytes@0.213.1": { - "integrity": "97f133c5bfb18b4522675e0822089de91e32618a4d8c2fcea8e175aca8f1f65c" - }, - "@std/bytes@1.0.2": { - "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" + "@std/bytes@1.0.4": { + "integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc" }, - "@std/cli@0.218.2": { - "integrity": "7877f41b4369f51b2ee06132b678ceee703051671c0fcea9058c1c0242652098", - "dependencies": [ - "jsr:@std/assert@~0.218.2" - ] + "@std/cli@1.0.11": { + "integrity": "ec219619fdcd31bcf0d8e53bee1e2706ec9a02f70255365a094f69755dadd340" }, - "@std/encoding@0.218.2": { - "integrity": "da55a763c29bf0dbf06fd286430b358266eb99c28789d89fe9a3e28edecb8d8e" + "@std/encoding@1.0.6": { + "integrity": "ca87122c196e8831737d9547acf001766618e78cd8c33920776c7f5885546069" }, - "@std/fmt@0.213.0": { - "integrity": "4623054b309ee60e37cc3db2bc55385aa129bb899f0c50e912d79a763aee6b6a" - }, - "@std/fmt@0.218.2": { - "integrity": "99526449d2505aa758b6cbef81e7dd471d8b28ec0dcb1491d122b284c548788a" - }, - "@std/fs@0.213.0": { - "integrity": "607ed7611e61e33179e2a6a7c60c086d6fef3b79438c403c51a336d0ca4e162d", - "dependencies": [ - "jsr:@std/assert@0.213", - "jsr:@std/path@0.213" - ] - }, - "@std/fs@0.218.2": { - "integrity": "dd9431453f7282e8c577cc22c9e6d036055a9a980b5549f887d6012969fabcca", - "dependencies": [ - "jsr:@std/assert@~0.218.2", - "jsr:@std/path@~0.218.2" - ] + "@std/fmt@1.0.4": { + "integrity": "e14fe5bedee26f80877e6705a97a79c7eed599e81bb1669127ef9e8bc1e29a74" }, - "@std/fs@1.0.8": { - "integrity": "161c721b6f9400b8100a851b6f4061431c538b204bb76c501d02c508995cffe0", + "@std/fs@1.0.10": { + "integrity": "bf041f9d7a0a460817f0421be8946d0e06011b3433e6c83a215628de5e3c7c2c", "dependencies": [ "jsr:@std/path@^1.0.8" ] }, - "@std/io@0.213.0": { - "integrity": "e78db92000e718c4b37e3c5f8a854a7c8d66cf4f1ab81d2c5c834172e66191cb", - "dependencies": [ - "jsr:@std/assert@0.213", - "jsr:@std/bytes@0.213" - ] - }, - "@std/path@0.213.0": { - "integrity": "9488ae5c052132130ac8ae406aafe1db2a407d6839ac0e8896c99497ff59087e", - "dependencies": [ - "jsr:@std/assert@0.213" - ] - }, - "@std/path@0.218.2": { - "integrity": "b568fd923d9e53ad76d17c513e7310bda8e755a3e825e6289a0ce536404e2662", - "dependencies": [ - "jsr:@std/assert@~0.218.2" - ] + "@std/internal@1.0.5": { + "integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba" }, "@std/path@1.0.8": { "integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be" }, - "@std/streams@0.213.0": { - "integrity": "14519c7ce5f4c88de2e365f4bf1b8371c942a9fe0e1f9a0b9e10a56e3c3e3877", + "@std/streams@1.0.8": { + "integrity": "b41332d93d2cf6a82fe4ac2153b930adf1a859392931e2a19d9fabfb6f154fb3", "dependencies": [ - "jsr:@std/io@0.213" + "jsr:@std/bytes" ] }, - "@std/streams@1.0.6": { - "integrity": "022ed94e380d06b4d91c49eb70241b7289ab78b8c2b4c4bbb7eb265e4997c25c", + "@std/tar@0.1.4": { + "integrity": "1bc1f1f9bfd557e849b31d6521348fdf5848886d87c851f1f0f992d002fe0ff5", "dependencies": [ - "jsr:@std/bytes@^1.0.2" - ] - }, - "@std/streams@1.0.7": { - "integrity": "1a93917ca0c58c01b2bfb93647189229b1702677f169b6fb61ad6241cd2e499b", - "dependencies": [ - "jsr:@std/bytes@^1.0.2" - ] - }, - "@std/tar@0.1.2": { - "integrity": "98183102395decd6268253996177804f818580ef547a25b81da0e7cc334db708", - "dependencies": [ - "jsr:@std/streams@^1.0.7" + "jsr:@std/streams" ] } }, - "remote": { - "https://raw.githubusercontent.com/denoland/std/0352a3c441aefe39d1233b4be92c49e404963457/tar/untar_stream.ts": "abd9d92f39e1613a5c6b573ffad4081ae2ea4e613ac2856df5c5ee5b9bf3beab" - }, "workspace": { "dependencies": [ - "jsr:@david/dax@~0.39.2", + "jsr:@david/path@0.2", "jsr:@david/temp@~0.1.1", - "jsr:@std/assert@~0.218.2", - "jsr:@std/cli@~0.218.2", - "jsr:@std/encoding@~0.218.2", - "jsr:@std/fmt@~0.218.2", - "jsr:@std/fs@~0.218.2", - "jsr:@std/path@~0.218.2", - "jsr:@std/streams@^1.0.6", - "jsr:@std/tar@~0.1.2" + "jsr:@std/assert@^1.0.11", + "jsr:@std/cli@^1.0.11", + "jsr:@std/encoding@^1.0.6", + "jsr:@std/fmt@^1.0.4", + "jsr:@std/fs@^1.0.10", + "jsr:@std/path@^1.0.8", + "jsr:@std/streams@^1.0.8", + "jsr:@std/tar@~0.1.4" ] } } diff --git a/lib/args.test.ts b/lib/args.test.ts index b05f65e..c0297a3 100644 --- a/lib/args.test.ts +++ b/lib/args.test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import { type CommonBuild, parseArgs } from "./args.ts"; @@ -8,11 +8,12 @@ Deno.test("no default features", () => { "--features", "wasm", "--no-default-features", + "--inline", "--out", "js", ]); assertEquals( (args as CommonBuild).cargoFlags, - ["--no-default-features", "--features", "wasm"], + ["--no-default-features", "--features", "wasm", "--inline"], ); }); diff --git a/lib/args.ts b/lib/args.ts index 599a3d2..f026838 100644 --- a/lib/args.ts +++ b/lib/args.ts @@ -1,6 +1,7 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -import { parseArgs as parseFlags } from "@std/cli/parse_args"; +import { parseArgs as parseFlags } from "@std/cli/parse-args"; +import { Path } from "@david/path"; export type Command = NewCommand | BuildCommand | CheckCommand | HelpCommand; @@ -12,11 +13,12 @@ export interface HelpCommand { } export interface CommonBuild { - outDir: string; + outDir: Path; bindingJsFileExt: "js" | "mjs"; profile: "debug" | "release"; project: string | undefined; isOpt: boolean; + inline: boolean; cargoFlags: string[]; } @@ -29,7 +31,9 @@ export interface CheckCommand extends CommonBuild { } export function parseArgs(rawArgs: string[]): Command { - const flags = parseFlags(rawArgs, { "--": true }); + const flags = parseFlags(rawArgs, { + "--": true, + }); if (flags.help || flags.h) return { kind: "help" }; switch (flags._[0]) { case "new": @@ -57,7 +61,7 @@ export function parseArgs(rawArgs: string[]): Command { function getCommonBuild(): CommonBuild { if (flags.sync) { throw new Error( - "The --sync flag is no longer supported now that Wasmbuild supports Wasm imports. Use an old version if you need it.", + "The --sync flag has been renamed to --inline.", ); } if (flags["no-cache"]) { @@ -69,8 +73,9 @@ export function parseArgs(rawArgs: string[]): Command { return { profile: flags.debug ? "debug" : "release", project: flags.p ?? flags.project, + inline: flags.inline, isOpt: !(flags["skip-opt"] ?? flags.debug == "debug"), - outDir: flags.out ?? "./lib", + outDir: new Path(flags.out ?? "./lib"), bindingJsFileExt: getBindingJsFileExt(), cargoFlags: getCargoFlags(), }; diff --git a/lib/bindgen.ts b/lib/bindgen.ts index 4635a31..2721d18 100644 --- a/lib/bindgen.ts +++ b/lib/bindgen.ts @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +import { createTempDirSync } from "@david/temp"; import { generate_bindgen } from "./wasmbuild.js"; -import * as path from "@std/path"; +import type { Path } from "@david/path"; export interface BindgenTextFileOutput { name: string; @@ -9,7 +10,6 @@ export interface BindgenTextFileOutput { } export interface BindgenOutput { - js: BindgenTextFileOutput; jsBg: BindgenTextFileOutput; ts: BindgenTextFileOutput; snippets: Map; @@ -22,7 +22,7 @@ export interface BindgenOutput { export async function generateBindgen({ libName, filePath, ext }: { libName: string; - filePath: string; + filePath: Path; ext: string; }) { // if wasmbuild is building itself, then we need to use the wasm-bindgen-cli @@ -31,7 +31,7 @@ export async function generateBindgen({ libName, filePath, ext }: { return generateForSelfBuild(filePath); } - const originalWasmBytes = await Deno.readFile(filePath); + const originalWasmBytes = filePath.readBytesSync(); return await generate_bindgen( libName, ext, @@ -39,54 +39,42 @@ export async function generateBindgen({ libName, filePath, ext }: { ) as BindgenOutput; } -async function generateForSelfBuild(filePath: string): Promise { +async function generateForSelfBuild(filePath: Path): Promise { // When upgrading wasm-bindgen within wasmbuild, we can't rely on // using the .wasm file because it will be out of date and not build, // so we revert to using the globally installed wasm-bindgen cli. // See https://github.com/denoland/wasmbuild/issues/51 for more details - const tempPath = await Deno.makeTempDir(); - try { - // note: ensure you have run `cargo install -f wasm-bindgen-cli` to upgrade - // to the latest version - const p = new Deno.Command("wasm-bindgen", { - args: [ - "--target", - "bundler", - "--out-dir", - tempPath, - filePath, - ], - }).spawn(); - const output = await p.status; - if (!output.success) { - throw new Error("Failed."); - } - const wasmBytes = await Deno.readFile( - path.join(tempPath, "wasmbuild_bg.wasm"), - ); - return { - js: { - name: "wasmbuild.js", - text: (await Deno.readTextFile(path.join(tempPath, "wasmbuild.js"))), - }, - jsBg: { - name: "wasmbuild_bg.js", - text: await Deno.readTextFile(path.join(tempPath, "wasmbuild_bg.js")), - }, - ts: { - name: "wasmbuild.d.ts", - text: await Deno.readTextFile(path.join(tempPath, "wasmbuild.d.ts")), - }, - localModules: new Map(), - snippets: new Map(), - wasm: { - name: "wasmbuild_bg.wasm", - bytes: Array.from(wasmBytes), - }, - }; - } finally { - await Deno.remove(tempPath, { - recursive: true, - }); + using tempDir = createTempDirSync(); + // note: ensure you have run `cargo install -f wasm-bindgen-cli` to upgrade + // to the latest version + const p = new Deno.Command("wasm-bindgen", { + args: [ + "--target", + "bundler", + "--out-dir", + tempDir.toString(), + filePath.toString(), + ], + }).spawn(); + const output = await p.status; + if (!output.success) { + throw new Error("Failed."); } + const wasmBytes = tempDir.join("wasmbuild_bg.wasm").readBytesSync(); + return { + jsBg: { + name: "wasmbuild_bg.js", + text: tempDir.join("wasmbuild_bg.js").readTextSync(), + }, + ts: { + name: "wasmbuild.d.ts", + text: tempDir.join("wasmbuild.d.ts").readTextSync(), + }, + localModules: new Map(), + snippets: new Map(), + wasm: { + name: "wasmbuild_bg.wasm", + bytes: Array.from(wasmBytes), + }, + }; } diff --git a/lib/commands/build_command.ts b/lib/commands/build_command.ts index 675c303..a477cc0 100644 --- a/lib/commands/build_command.ts +++ b/lib/commands/build_command.ts @@ -1,31 +1,33 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as colors from "@std/fmt/colors"; -import { emptyDir } from "@std/fs/empty_dir"; -import * as path from "@std/path"; -import { ensureDir } from "@std/fs"; +import * as base64 from "@std/encoding/base64"; import type { BuildCommand } from "../args.ts"; -import { runPreBuild } from "../pre_build.ts"; +import { + generatedHeader, + getFormattedText, + type PreBuildOutput, + runPreBuild, +} from "../pre_build.ts"; import { runWasmOpt } from "../wasmopt.ts"; +import type { Path } from "@david/path"; export async function runBuildCommand(args: BuildCommand) { const output = await runPreBuild(args); - await ensureDir(args.outDir); - await writeSnippets(); - - console.log(` write ${colors.yellow(output.bindingJs.path)}`); - await Deno.writeTextFile(output.bindingJs.path, output.bindingJs.text); - console.log(` write ${colors.yellow(output.bindingJsBg.path)}`); - await Deno.writeTextFile(output.bindingJsBg.path, output.bindingJsBg.text); - console.log(` write ${colors.yellow(output.bindingDts.path)}`); - await Deno.writeTextFile(output.bindingDts.path, output.bindingDts.text); - - if (output.wasmFileName != null) { - const wasmDest = path.join(args.outDir, output.wasmFileName); - await Deno.writeFile(wasmDest, new Uint8Array(output.bindgen.wasm.bytes)); - if (args.isOpt) { - await optimizeWasmFile(wasmDest); + args.outDir.ensureDirSync(); + writeSnippets(); + + const files = args.inline + ? await inlinePreBuild(output, args) + : await handleWasmModuleOutput(output, args); + + for (const file of files) { + console.log(` write ${colors.yellow(file.path.toString())}`); + if (typeof file.data === "string") { + file.path.writeTextSync(file.data); + } else { + file.path.writeSync(file.data); } } @@ -33,22 +35,7 @@ export async function runBuildCommand(args: BuildCommand) { `${colors.bold(colors.green("Finished"))} WebAssembly output`, ); - async function optimizeWasmFile(wasmFilePath: string) { - try { - console.log( - `${colors.bold(colors.green("Optimizing"))} .wasm file...`, - ); - await runWasmOpt(wasmFilePath); - } catch (err) { - console.error( - `${colors.bold(colors.red("Error"))} ` + - `running wasm-opt failed. Maybe skip with --skip-opt?\n\n${err}`, - ); - Deno.exit(1); - } - } - - async function writeSnippets() { + function writeSnippets() { const localModules = Array.from(output.bindgen.localModules); const snippets = Array.from(output.bindgen.snippets); @@ -56,29 +43,133 @@ export async function runBuildCommand(args: BuildCommand) { return; // don't create the snippets directory } - const snippetsDest = path.join(args.outDir, "snippets"); + const snippetsDest = args.outDir.join("snippets"); // start with a fresh directory in order to clear out any previously // created snippets which might have a different name - await emptyDir(snippetsDest); + snippetsDest.emptyDirSync(); for (const [name, text] of localModules) { - const filePath = path.join(snippetsDest, name); - const dirPath = path.dirname(filePath); - await Deno.mkdir(dirPath, { recursive: true }); - await Deno.writeTextFile(filePath, text); + const filePath = snippetsDest.join(name); + const dirPath = filePath.parentOrThrow(); + dirPath.mkdirSync({ recursive: true }); + filePath.writeTextSync(text); } for (const [identifier, list] of snippets) { if (list.length === 0) { continue; } - const dirPath = path.join(snippetsDest, identifier); - await Deno.mkdir(dirPath, { recursive: true }); + const dirPath = snippetsDest.join(identifier); + dirPath.mkdirSync({ recursive: true }); for (const [i, text] of list.entries()) { const name = `inline${i}.js`; - const filePath = path.join(dirPath, name); - await Deno.writeTextFile(filePath, text); + const filePath = dirPath.join(name); + filePath.writeTextSync(text); } } } } + +interface FileEntry { + path: Path; + data: string | Uint8Array; +} + +async function handleWasmModuleOutput( + output: PreBuildOutput, + args: BuildCommand, +): Promise { + return [{ + path: args.outDir.join( + `${output.crateName}.${args.bindingJsFileExt}`, + ), + data: await getFormattedText(`${generatedHeader} +// @ts-self-types="./${output.bindingDts.path.basename()}" + +// source-hash: ${output.sourceHash} +import * as wasm from "./${output.wasmFileName}"; +export * from "./${output.bindingJsBg.path.basename()}"; +import { __wbg_set_wasm } from "./${output.bindingJsBg.path.basename()}"; +__wbg_set_wasm(wasm); +`), + }, { + path: output.bindingJsBg.path, + data: output.bindingJsBg.text, + }, { + path: output.bindingDts.path, + data: output.bindingDts.text, + }, { + path: args.outDir.join(output.wasmFileName), + data: await getWasmBytes(output, args), + }]; +} + +async function inlinePreBuild( + output: PreBuildOutput, + args: BuildCommand, +): Promise { + const wasmBytes = await getWasmBytes(output, args); + + return [{ + path: args.outDir.join( + `${output.crateName}.${args.bindingJsFileExt}`, + ), + data: await getFormattedText(`${generatedHeader} +// @ts-self-types="./${output.bindingDts.path.basename()}" + +// source-hash: ${output.sourceHash} +import * as imports from "./${output.bindingJsBg.path.basename()}"; +const bytes = base64decode("\\\n${ + base64.encodeBase64(wasmBytes).replace(/.{78}/g, "$&\\\n") + }\\\n"); +const wasmModule = new WebAssembly.Module(bytes); +const wasm = new WebAssembly.Instance(wasmModule, { + "./${output.bindingJsBg.path.basename()}": imports, +}); + +export * from "./${output.bindingJsBg.path.basename()}"; +import { __wbg_set_wasm } from "./${output.bindingJsBg.path.basename()}"; +__wbg_set_wasm(wasm.exports); + +function base64decode(b64) { + const binString = atob(b64); + const size = binString.length; + const bytes = new Uint8Array(size); + for (let i = 0; i < size; i++) { + bytes[i] = binString.charCodeAt(i); + } + return bytes; +} +`), + }, { + path: output.bindingJsBg.path, + data: output.bindingJsBg.text, + }, { + path: output.bindingDts.path, + data: output.bindingDts.text, + }]; +} + +async function getWasmBytes(output: PreBuildOutput, args: BuildCommand) { + const wasmBytes = new Uint8Array(output.bindgen.wasm.bytes); + if (args.isOpt) { + return await optimizeWasmFile(wasmBytes); + } else { + return wasmBytes; + } +} + +async function optimizeWasmFile(fileBytes: Uint8Array) { + try { + console.log( + `${colors.bold(colors.green("Optimizing"))} .wasm file...`, + ); + return await runWasmOpt(fileBytes); + } catch (err) { + console.error( + `${colors.bold(colors.red("Error"))} ` + + `running wasm-opt failed. Maybe skip with --skip-opt?\n\n${err}`, + ); + Deno.exit(1); + } +} diff --git a/lib/commands/check_command.ts b/lib/commands/check_command.ts index 0950b39..c4fe941 100644 --- a/lib/commands/check_command.ts +++ b/lib/commands/check_command.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as colors from "@std/fmt/colors"; import type { CheckCommand } from "../args.ts"; @@ -6,7 +6,7 @@ import { runPreBuild } from "../pre_build.ts"; export async function runCheckCommand(args: CheckCommand) { const output = await runPreBuild(args); - const originalHash = await getOriginalSourceHash(); + const originalHash = getOriginalSourceHash(); if (originalHash === output.sourceHash) { console.log( `${colors.bold(colors.green("Success"))} ` + @@ -20,13 +20,15 @@ export async function runCheckCommand(args: CheckCommand) { Deno.exit(1); } - async function getOriginalSourceHash() { + function getOriginalSourceHash() { + const filePath = args.outDir.join( + `${output.crateName}.${args.bindingJsFileExt}`, + ); try { - return getSourceHashFromText( - await Deno.readTextFile(output.bindingJs.path), - ); + return getSourceHashFromText(filePath.readTextSync()); } catch (err) { if (err instanceof Deno.errors.NotFound) { + console.warn(`${colors.yellow("Warning")} could not find ${filePath}`); return undefined; } else { throw err; diff --git a/lib/commands/new_command.ts b/lib/commands/new_command.ts index 795b453..e5cce46 100644 --- a/lib/commands/new_command.ts +++ b/lib/commands/new_command.ts @@ -1,14 +1,16 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as colors from "@std/fmt/colors"; -import { ensureDir } from "@std/fs/ensure_dir"; import { versions } from "../versions.ts"; -import { pathExists } from "../helpers.ts"; +import { Path } from "@david/path"; export async function runNewCommand() { await checkIfRequiredToolsExist(); - if (await pathExists("./rs_lib")) { + const rootDir = new Path(Deno.cwd()); + const rsLibDir = rootDir.join("rs_lib"); + + if (rsLibDir.existsSync()) { console.log( `${ colors.bold(colors.red("Error")) @@ -21,10 +23,9 @@ export async function runNewCommand() { `${colors.bold(colors.green("Creating"))} rs_lib...`, ); - if (!await pathExists("./Cargo.toml")) { - await Deno.writeTextFile( - "./Cargo.toml", - `[workspace] + writeIfNotExists( + rootDir.join("Cargo.toml"), + `[workspace] resolver = "2" members = [ "rs_lib", @@ -36,31 +37,30 @@ incremental = true lto = true opt-level = "z" `, - ); - } - if (!await pathExists("./.rustfmt.toml")) { - await Deno.writeTextFile( - "./.rustfmt.toml", - `max_width = 80 + ); + + writeIfNotExists( + rootDir.join(".rustfmt.toml"), + `max_width = 80 tab_spaces = 2 edition = "2021" `, - ); - } + ); - let gitIgnoreText = await getFileTextIfExists("./.gitignore") ?? ""; + const gitIgnoreFile = rootDir.join(".gitignore"); + let gitIgnoreText = gitIgnoreFile.readMaybeTextSync() ?? ""; if (!/^\/target$/m.test(gitIgnoreText)) { gitIgnoreText = gitIgnoreText.trim(); if (gitIgnoreText.length > 0) { gitIgnoreText = gitIgnoreText + "\n"; } gitIgnoreText += "/target\n"; - await Deno.writeTextFile("./.gitignore", gitIgnoreText); + gitIgnoreFile.writeTextSync(gitIgnoreText); } - await ensureDir("./rs_lib/src"); - await Deno.writeTextFile( - "./rs_lib/Cargo.toml", + const srcDir = rsLibDir.join("src"); + srcDir.ensureDirSync(); + rsLibDir.join("./Cargo.toml").writeTextSync( `[package] name = "rs_lib" version = "0.0.0" @@ -73,8 +73,8 @@ crate-type = ["cdylib"] wasm-bindgen = "=${versions["wasm-bindgen"]}" `, ); - await Deno.writeTextFile( - "./rs_lib/src/lib.rs", + + srcDir.join("lib.rs").writeTextSync( `use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -117,11 +117,11 @@ mod tests { } `, ); - if (!await pathExists("./mod.js")) { - // use a .js file for the most compatibility out of the box (ex. browsers) - await Deno.writeTextFile( - "./mod.js", - `import { add, Greeter } from "./lib/rs_lib.js"; + + // use a .js file for the most compatibility out of the box (ex. browsers) + writeIfNotExists( + rootDir.join("mod.js"), + `import { add, Greeter } from "./lib/rs_lib.js"; // adds console.log(add(1, 1)); @@ -130,23 +130,18 @@ console.log(add(1, 1)); const greeter = new Greeter("world"); console.log(greeter.greet()); `, - ); - } + ); + console.log("%cTo get started run:", "color:yellow"); console.log("deno task wasmbuild"); console.log("deno run mod.js"); } -async function getFileTextIfExists(path: string) { - try { - return await Deno.readTextFile(path); - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - return undefined; - } else { - throw err; - } +function writeIfNotExists(path: Path, text: string) { + if (path.existsSync()) { + return; } + path.writeTextSync(text); } async function checkIfRequiredToolsExist() { diff --git a/lib/helpers.ts b/lib/helpers.ts deleted file mode 100644 index 6be141b..0000000 --- a/lib/helpers.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2018-2024 the Deno authors. MIT license. - -export async function pathExists(path: string) { - try { - await Deno.stat(path); - return true; - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - return false; - } else { - throw err; - } - } -} diff --git a/lib/manifest.ts b/lib/manifest.ts index 540f37b..461b441 100644 --- a/lib/manifest.ts +++ b/lib/manifest.ts @@ -1,8 +1,8 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Sha1 } from "./utils/sha1.ts"; -import * as path from "@std/path"; -import { expandGlob } from "@std/fs/expand_glob"; +import { expandGlob } from "@std/fs/expand-glob"; +import { Path } from "@david/path"; export interface CargoMetadata { packages: CargoPackageMetadata[]; @@ -43,11 +43,11 @@ export interface CargoPackageTarget { } export async function getCargoWorkspace( - directory: string, + directory: Path, cargoFlags: string[], ) { const p = new Deno.Command("cargo", { - cwd: directory, + cwd: directory.toString(), args: ["metadata", "--format-version", "1", ...cargoFlags], stdout: "piped", }); @@ -153,7 +153,7 @@ export class WasmCrate { } get rootFolder() { - return path.dirname(this.#pkg.manifest_path); + return new Path(this.#pkg.manifest_path).parentOrThrow(); } async getSourcesHash() { @@ -162,7 +162,7 @@ export class WasmCrate { paths.sort(); const hasher = new Sha1(); for (const path of paths) { - const fileText = await Deno.readTextFile(path); + const fileText = path.readTextSync(); // standardize file paths so this is not subject to // however git is configured to checkout files hasher.update(fileText.replace(/\r?\n/g, "\n")); @@ -174,12 +174,12 @@ export class WasmCrate { const paths = []; for await ( const entry of expandGlob("**/{*.rs,Cargo.toml}", { - root: this.rootFolder, + root: this.rootFolder.toString(), exclude: ["./target"], }) ) { if (entry.isFile) { - paths.push(entry.path); + paths.push(new Path(entry.path)); } } return paths; diff --git a/lib/pre_build.ts b/lib/pre_build.ts index f9624cf..41ce814 100644 --- a/lib/pre_build.ts +++ b/lib/pre_build.ts @@ -1,44 +1,40 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import type { BuildCommand, CheckCommand } from "./args.ts"; import * as colors from "@std/fmt/colors"; -import * as path from "@std/path"; import { Sha1 } from "./utils/sha1.ts"; -import { getCargoWorkspace, type WasmCrate } from "./manifest.ts"; +import { getCargoWorkspace } from "./manifest.ts"; import { verifyVersions } from "./versions.ts"; import { type BindgenOutput, generateBindgen } from "./bindgen.ts"; -import { pathExists } from "./helpers.ts"; +import { Path } from "@david/path"; export type { BindgenOutput } from "./bindgen.ts"; -const generatedHeader = `// @generated file from wasmbuild -- do not edit +export const generatedHeader = `// @generated file from wasmbuild -- do not edit // @ts-nocheck: generated // deno-lint-ignore-file // deno-fmt-ignore-file`; export interface PreBuildOutput { + crateName: string; bindgen: BindgenOutput; - bindingJs: { - path: string; - text: string; - }; bindingJsBg: { - path: string; + path: Path; text: string; }; bindingDts: { - path: string; + path: Path; text: string; }; sourceHash: string; - wasmFileName: string | undefined; + wasmFileName: string; } export async function runPreBuild( args: CheckCommand | BuildCommand, ): Promise { const home = Deno.env.get("HOME"); - const root = Deno.cwd(); - if (!await pathExists(path.join(root, "Cargo.toml"))) { + const root = new Path(Deno.cwd()); + if (!root.join("Cargo.toml").existsSync()) { console.error( "%cConsider running `deno task wasmbuild new` to get started", "color: yellow", @@ -122,8 +118,7 @@ export async function runPreBuild( const bindgenOutput = await generateBindgen({ libName: crate.libName, ext: args.bindingJsFileExt, - filePath: path.join( - workspace.metadata.target_directory, + filePath: new Path(workspace.metadata.target_directory).join( `wasm32-unknown-unknown/${args.profile}/${crate.libName}.wasm`, ), }); @@ -132,25 +127,19 @@ export async function runPreBuild( `${colors.bold(colors.green("Generating"))} lib JS bindings...`, ); - const { bindingJsText, sourceHash } = await getBindingJsOutput( - crate, - bindgenOutput, - ); + const sourceHash = await getHash(); return { + crateName: crate.libName, bindgen: bindgenOutput, - bindingJs: { - path: path.join(args.outDir, bindgenOutput.js.name), - text: bindingJsText, - }, bindingJsBg: { - path: path.join(args.outDir, bindgenOutput.jsBg.name), + path: args.outDir.join(bindgenOutput.jsBg.name), text: `${generatedHeader}\n\n${await getFormattedText( bindgenOutput.jsBg.text, )}`, }, bindingDts: { - path: path.join(args.outDir, bindgenOutput.ts.name), + path: args.outDir.join(bindgenOutput.ts.name), text: `// @generated file from wasmbuild -- do not edit // deno-lint-ignore-file // deno-fmt-ignore-file @@ -160,26 +149,6 @@ ${await getFormattedText(getLibraryDts(bindgenOutput))}`, sourceHash, wasmFileName: bindgenOutput.wasm.name, }; -} - -async function getBindingJsOutput( - crate: WasmCrate, - bindgenOutput: BindgenOutput, -) { - const sourceHash = await getHash(); - const header = `${generatedHeader} -// @ts-self-types="./${bindgenOutput.ts.name}" -`; - const genText = bindgenOutput.js.text; - const bodyText = await getFormattedText(` -// source-hash: ${sourceHash} -${genText} -`); - - return { - bindingJsText: `${header}\n${bodyText}`, - sourceHash, - }; async function getHash() { // Create a hash of all the sources, snippets, and local modules @@ -201,7 +170,7 @@ ${genText} } } -async function getFormattedText(inputText: string) { +export async function getFormattedText(inputText: string) { const denoFmtCmdArgs = [ "fmt", "--quiet", diff --git a/lib/versions.test.ts b/lib/versions.test.ts index 108f21e..50fc31d 100644 --- a/lib/versions.test.ts +++ b/lib/versions.test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** * This module contains logic for ensuring that some other Rust dependencies @@ -8,7 +8,7 @@ * @see {@link https://github.com/rustwasm/wasm-bindgen/pull/2913#issuecomment-1139100835} */ -import { assertThrows } from "@std/assert/assert_throws"; +import { assertThrows } from "@std/assert"; import { verifyVersions, versions } from "./versions.ts"; Deno.test("should verify when all correct", () => { diff --git a/lib/versions.ts b/lib/versions.ts index 32d9a90..0a8403a 100644 --- a/lib/versions.ts +++ b/lib/versions.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. interface WasmCrate { name: string; diff --git a/lib/wasmbuild.internal.js b/lib/wasmbuild.internal.js deleted file mode 100644 index 4c8e37b..0000000 --- a/lib/wasmbuild.internal.js +++ /dev/null @@ -1,262 +0,0 @@ -// @generated file from wasmbuild -- do not edit -// @ts-nocheck: generated -// deno-lint-ignore-file -// deno-fmt-ignore-file - -let wasm; -export function __wbg_set_wasm(val) { - wasm = val; -} - -const heap = new Array(128).fill(undefined); - -heap.push(undefined, null, true, false); - -function getObject(idx) { - return heap[idx]; -} - -let WASM_VECTOR_LEN = 0; - -let cachedUint8ArrayMemory0 = null; - -function getUint8ArrayMemory0() { - if ( - cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0 - ) { - cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); - } - return cachedUint8ArrayMemory0; -} - -const lTextEncoder = typeof TextEncoder === "undefined" - ? (0, module.require)("util").TextEncoder - : TextEncoder; - -let cachedTextEncoder = new lTextEncoder("utf-8"); - -const encodeString = typeof cachedTextEncoder.encodeInto === "function" - ? function (arg, view) { - return cachedTextEncoder.encodeInto(arg, view); - } - : function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length, - }; - }; - -function passStringToWasm0(arg, malloc, realloc) { - if (realloc === undefined) { - const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length, 1) >>> 0; - getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let len = arg.length; - let ptr = malloc(len, 1) >>> 0; - - const mem = getUint8ArrayMemory0(); - - let offset = 0; - - for (; offset < len; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - - if (offset !== len) { - if (offset !== 0) { - arg = arg.slice(offset); - } - ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; - const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); - const ret = encodeString(arg, view); - - offset += ret.written; - ptr = realloc(ptr, len, offset, 1) >>> 0; - } - - WASM_VECTOR_LEN = offset; - return ptr; -} - -let cachedDataViewMemory0 = null; - -function getDataViewMemory0() { - if ( - cachedDataViewMemory0 === null || - cachedDataViewMemory0.buffer.detached === true || - (cachedDataViewMemory0.buffer.detached === undefined && - cachedDataViewMemory0.buffer !== wasm.memory.buffer) - ) { - cachedDataViewMemory0 = new DataView(wasm.memory.buffer); - } - return cachedDataViewMemory0; -} - -let heap_next = heap.length; - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -const lTextDecoder = typeof TextDecoder === "undefined" - ? (0, module.require)("util").TextDecoder - : TextDecoder; - -let cachedTextDecoder = new lTextDecoder("utf-8", { - ignoreBOM: true, - fatal: true, -}); - -cachedTextDecoder.decode(); - -function getStringFromWasm0(ptr, len) { - ptr = ptr >>> 0; - return cachedTextDecoder.decode( - getUint8ArrayMemory0().subarray(ptr, ptr + len), - ); -} - -function dropObject(idx) { - if (idx < 132) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - -function passArray8ToWasm0(arg, malloc) { - const ptr = malloc(arg.length * 1, 1) >>> 0; - getUint8ArrayMemory0().set(arg, ptr / 1); - WASM_VECTOR_LEN = arg.length; - return ptr; -} -/** - * @param {string} name - * @param {string} ext - * @param {Uint8Array} wasm_bytes - * @returns {any} - */ -export function generate_bindgen(name, ext, wasm_bytes) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passStringToWasm0( - name, - wasm.__wbindgen_malloc, - wasm.__wbindgen_realloc, - ); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passStringToWasm0( - ext, - wasm.__wbindgen_malloc, - wasm.__wbindgen_realloc, - ); - const len1 = WASM_VECTOR_LEN; - const ptr2 = passArray8ToWasm0(wasm_bytes, wasm.__wbindgen_malloc); - const len2 = WASM_VECTOR_LEN; - wasm.generate_bindgen(retptr, ptr0, len0, ptr1, len1, ptr2, len2); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true); - if (r2) { - throw takeObject(r1); - } - return takeObject(r0); - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } -} - -export function __wbg_String_8f0eb39a4a4c2f66(arg0, arg1) { - const ret = String(getObject(arg1)); - const ptr1 = passStringToWasm0( - ret, - wasm.__wbindgen_malloc, - wasm.__wbindgen_realloc, - ); - const len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); -} - -export function __wbg_new_405e22f390576ce2() { - const ret = new Object(); - return addHeapObject(ret); -} - -export function __wbg_new_5e0be73521bc8c17() { - const ret = new Map(); - return addHeapObject(ret); -} - -export function __wbg_new_78feb108b6472713() { - const ret = new Array(); - return addHeapObject(ret); -} - -export function __wbg_new_c68d7209be747379(arg0, arg1) { - const ret = new Error(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); -} - -export function __wbg_set_37837023f3d740e8(arg0, arg1, arg2) { - getObject(arg0)[arg1 >>> 0] = takeObject(arg2); -} - -export function __wbg_set_3f1d0b984ed272ed(arg0, arg1, arg2) { - getObject(arg0)[takeObject(arg1)] = takeObject(arg2); -} - -export function __wbg_set_8fc6bf8a5b1071d1(arg0, arg1, arg2) { - const ret = getObject(arg0).set(getObject(arg1), getObject(arg2)); - return addHeapObject(ret); -} - -export function __wbindgen_error_new(arg0, arg1) { - const ret = new Error(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); -} - -export function __wbindgen_is_string(arg0) { - const ret = typeof (getObject(arg0)) === "string"; - return ret; -} - -export function __wbindgen_number_new(arg0) { - const ret = arg0; - return addHeapObject(ret); -} - -export function __wbindgen_object_clone_ref(arg0) { - const ret = getObject(arg0); - return addHeapObject(ret); -} - -export function __wbindgen_object_drop_ref(arg0) { - takeObject(arg0); -} - -export function __wbindgen_string_new(arg0, arg1) { - const ret = getStringFromWasm0(arg0, arg1); - return addHeapObject(ret); -} - -export function __wbindgen_throw(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); -} diff --git a/lib/wasmbuild.js b/lib/wasmbuild.js index 699abab..e7ed561 100644 --- a/lib/wasmbuild.js +++ b/lib/wasmbuild.js @@ -3,9 +3,9 @@ // deno-lint-ignore-file // deno-fmt-ignore-file // @ts-self-types="./wasmbuild.d.ts" +// source-hash: 42abdfbeb5cf210c856f23fe256f0194e6faf8d0 -// source-hash: d5fff448b0ce0d83629b673dbe53bfcbfd1838ba -import * as wasm from "./wasmbuild.wasm"; -export * from "./wasmbuild.internal.js"; -import { __wbg_set_wasm } from "./wasmbuild.internal.js"; +import * as wasm from "./wasmbuild_bg.wasm"; +export * from "./wasmbuild_bg.js"; +import { __wbg_set_wasm } from "./wasmbuild_bg.js"; __wbg_set_wasm(wasm); diff --git a/lib/wasmbuild.wasm b/lib/wasmbuild.wasm deleted file mode 100644 index f517273..0000000 Binary files a/lib/wasmbuild.wasm and /dev/null differ diff --git a/lib/wasmbuild_bg.js b/lib/wasmbuild_bg.js index 4ff21d4..4c8e37b 100644 --- a/lib/wasmbuild_bg.js +++ b/lib/wasmbuild_bg.js @@ -100,6 +100,17 @@ function getDataViewMemory0() { return cachedDataViewMemory0; } +let heap_next = heap.length; + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + const lTextDecoder = typeof TextDecoder === "undefined" ? (0, module.require)("util").TextDecoder : TextDecoder; @@ -118,17 +129,6 @@ function getStringFromWasm0(ptr, len) { ); } -let heap_next = heap.length; - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - function dropObject(idx) { if (idx < 132) return; heap[idx] = heap_next; @@ -195,27 +195,27 @@ export function __wbg_String_8f0eb39a4a4c2f66(arg0, arg1) { getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); } -export function __wbg_new_5bd3cfec88bd010e(arg0, arg1) { - const ret = new Error(getStringFromWasm0(arg0, arg1)); +export function __wbg_new_405e22f390576ce2() { + const ret = new Object(); return addHeapObject(ret); } -export function __wbg_new_9e6542cc3fe4b09e() { - const ret = new Array(); +export function __wbg_new_5e0be73521bc8c17() { + const ret = new Map(); return addHeapObject(ret); } -export function __wbg_new_dbb4955149975b18() { - const ret = new Object(); +export function __wbg_new_78feb108b6472713() { + const ret = new Array(); return addHeapObject(ret); } -export function __wbg_new_efea5718d1896ea2() { - const ret = new Map(); +export function __wbg_new_c68d7209be747379(arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); return addHeapObject(ret); } -export function __wbg_set_0ccc5fa791d83f2d(arg0, arg1, arg2) { +export function __wbg_set_37837023f3d740e8(arg0, arg1, arg2) { getObject(arg0)[arg1 >>> 0] = takeObject(arg2); } @@ -223,7 +223,7 @@ export function __wbg_set_3f1d0b984ed272ed(arg0, arg1, arg2) { getObject(arg0)[takeObject(arg1)] = takeObject(arg2); } -export function __wbg_set_9b8ce78fa3e7ad0e(arg0, arg1, arg2) { +export function __wbg_set_8fc6bf8a5b1071d1(arg0, arg1, arg2) { const ret = getObject(arg0).set(getObject(arg1), getObject(arg2)); return addHeapObject(ret); } diff --git a/lib/wasmbuild_bg.wasm b/lib/wasmbuild_bg.wasm index fd34cd6..cb3bfc7 100644 Binary files a/lib/wasmbuild_bg.wasm and b/lib/wasmbuild_bg.wasm differ diff --git a/lib/wasmopt.ts b/lib/wasmopt.ts index d1dffb3..d9f32a8 100644 --- a/lib/wasmopt.ts +++ b/lib/wasmopt.ts @@ -1,27 +1,33 @@ -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { UntarStream } from "@std/tar/untar-stream"; -import { ensureDir } from "@std/fs/ensure_dir"; import * as colors from "@std/fmt/colors"; -import * as path from "@std/path"; +import { createTempFileSync } from "@david/temp"; +import { Path } from "@david/path"; const wasmOptFileName = Deno.build.os === "windows" ? "wasm-opt.exe" : "wasm-opt"; -const tag = "version_109"; +const tag = "version_121"; -export async function runWasmOpt(filePath: string) { +export async function runWasmOpt(fileBytes: Uint8Array) { const binPath = await getWasmOptBinaryPath(); - const p = new Deno.Command(binPath, { - args: ["-Oz", filePath, "-o", filePath], + using outputTempFile = createTempFileSync(); + using inputTempFile = createTempFileSync(); + inputTempFile.writeSync(fileBytes); + const p = new Deno.Command(binPath.toString(), { + args: ["-Oz", inputTempFile.toString(), "-o", outputTempFile.toString()], + stdin: "inherit", stderr: "inherit", stdout: "inherit", }).spawn(); - const output = await p.status; - if (!output.success) { + const status = await p.status; + + if (!status.success) { throw new Error(`error executing wasmopt`); } + return outputTempFile.readBytesSync(); } async function fetchWithRetries(url: URL | string, maxRetries = 5) { @@ -46,20 +52,20 @@ async function fetchWithRetries(url: URL | string, maxRetries = 5) { } async function getWasmOptBinaryPath() { - const cacheDirPath = cacheDir(); - if (!cacheDirPath) { + const cacheDirPathText = cacheDir(); + if (!cacheDirPathText) { throw new Error("Could not find cache directory."); } - const tempDirPath = path.join(cacheDirPath, "wasmbuild", tag); - const wasmOptExePath = path.join( - tempDirPath, + const cacheDirPath = new Path(cacheDirPathText); + const tempDirPath = cacheDirPath.join("wasmbuild", tag); + const wasmOptExePath = tempDirPath.join( `binaryen-${tag}/bin`, wasmOptFileName, ); - if (!(await fileExists(wasmOptExePath))) { + if (!wasmOptExePath.existsSync()) { await downloadBinaryen(tempDirPath); - if (!(await fileExists(wasmOptExePath))) { + if (!wasmOptExePath.existsSync()) { throw new Error( `For some reason the wasm-opt executable did not exist after downloading at ${wasmOptExePath}.`, ); @@ -69,20 +75,7 @@ async function getWasmOptBinaryPath() { return wasmOptExePath; } -async function fileExists(path: string) { - try { - await Deno.stat(path); - return true; - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - return false; - } else { - throw err; - } - } -} - -async function downloadBinaryen(tempPath: string) { +async function downloadBinaryen(tempPath: Path) { console.log( `${colors.bold(colors.green("Downloading"))} wasm-opt binary...`, ); @@ -97,9 +90,9 @@ async function downloadBinaryen(tempPath: string) { entry.path.endsWith(wasmOptFileName) || entry.path.endsWith(".dylib") ) { - const fileName = path.join(tempPath, entry.path); - await ensureDir(path.dirname(fileName)); - using file = await Deno.open(fileName, { + const filePath = tempPath.join(entry.path); + filePath.parentOrThrow().ensureDirSync(); + using file = filePath.openSync({ create: true, write: true, mode: 0o755, diff --git a/main.ts b/main.ts index 2531273..2c76b7a 100644 --- a/main.ts +++ b/main.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-run --allow-read --allow-write --allow-env -// Copyright 2018-2024 the Deno authors. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { parseArgs } from "./lib/args.ts"; import { runNewCommand } from "./lib/commands/new_command.ts"; diff --git a/rs_lib/src/lib.rs b/rs_lib/src/lib.rs index 1e3b905..c450880 100644 --- a/rs_lib/src/lib.rs +++ b/rs_lib/src/lib.rs @@ -28,7 +28,6 @@ pub struct BindgenBytesFileOutput { #[derive(serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct Output { - pub js: BindgenTextFileOutput, pub js_bg: BindgenTextFileOutput, pub ts: Option, pub snippets: HashMap>, @@ -65,16 +64,6 @@ fn inner(name: &str, ext: &str, wasm_bytes: Vec) -> Result { } Ok(Output { - js: BindgenTextFileOutput { - name: format!("{}.{}", name, ext), - text: format!( - "import * as wasm from \"./{name}.wasm\"; -export * from \"./{name}.internal.{ext}\"; -import {{ __wbg_set_wasm }} from \"./{name}.internal.{ext}\"; -__wbg_set_wasm(wasm); -" - ), - }, js_bg: BindgenTextFileOutput { name: format!("{}.internal.{}", name, ext), text: x.js().to_string(), diff --git a/tests/.gitignore b/tests/.gitignore index d5d0542..0795843 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,4 @@ target/ lib_out_js_file +lib_inline lib diff --git a/tests/cli_test.ts b/tests/cli_test.ts index 06d291d..58d64b4 100644 --- a/tests/cli_test.ts +++ b/tests/cli_test.ts @@ -1,20 +1,20 @@ -import * as path from "@std/path"; import { createTempDirSync } from "@david/temp"; +import { Path } from "@david/path"; -const rootFolder = path.dirname( - path.dirname(path.fromFileUrl(import.meta.url)), -); +const rootFolder = new Path(import.meta.dirname!).parentOrThrow(); Deno.test("should create a new wasmbuild project, build it, and run it", async () => { using tempDir = createTempDirSync(); - await tempDir.join("deno.json").writeText( + tempDir.join("deno.json").writeTextSync( `{ "tasks": { "wasmbuild": "${ Deno.execPath().replace(/\\/g, "\\\\") - } run -A ${path.join(rootFolder, "main.ts").replace(/\\/g, "\\\\")}" }}\n`, + } run -A ${ + rootFolder.join("main.ts").toString().replace(/\\/g, "\\\\") + }" }}\n`, ); await runCommand("deno", "task", "wasmbuild", "new"); await runCommand("deno", "task", "wasmbuild"); - await tempDir.join("test.ts").writeText(` + tempDir.join("test.ts").writeTextSync(` import { add } from "./lib/rs_lib.js"; Deno.test("should add values", async () => { diff --git a/tests/test.ts b/tests/test.ts index 41c4baf..cc029fd 100644 --- a/tests/test.ts +++ b/tests/test.ts @@ -1,6 +1,11 @@ import { assertEquals } from "@std/assert"; import * as wasm from "./lib/deno_test.js"; +import * as wasm2 from "./lib_inline/deno_test.js"; Deno.test("test works export", () => { assertEquals(wasm.greet("Deno"), "Hello, Deno! Result: 3"); }); + +Deno.test("test inline works", () => { + assertEquals(wasm2.greet("Deno"), "Hello, Deno! Result: 3"); +});