Skip to content

Commit 9c5a6df

Browse files
authored
Merge pull request #2176 from jakobhellermann/deno-target
add deno target
2 parents 36dcbb8 + 665785e commit 9c5a6df

File tree

17 files changed

+415
-110
lines changed

17 files changed

+415
-110
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ members = [
6565
"examples/char",
6666
"examples/closures",
6767
"examples/console_log",
68+
"examples/deno",
6869
"examples/dom",
6970
"examples/duck-typed-interfaces",
7071
"examples/fetch",

azure-pipelines.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ jobs:
167167
- script: mv _package.json package.json && npm install && rm package.json
168168
displayName: "run npm install"
169169
- script: |
170-
for dir in `ls examples | grep -v README | grep -v asm.js | grep -v raytrace | grep -v without-a-bundler | grep -v websockets | grep -v webxr`; do
170+
for dir in `ls examples | grep -v README | grep -v asm.js | grep -v raytrace | grep -v without-a-bundler | grep -v websockets | grep -v webxr | grep -v deno`; do
171171
(cd examples/$dir &&
172172
ln -fs ../../node_modules . &&
173173
npm run build -- --output-path $BUILD_ARTIFACTSTAGINGDIRECTORY/exbuild/$dir) || exit 1;
@@ -178,6 +178,16 @@ jobs:
178178
artifactName: examples1
179179
targetPath: '$(Build.ArtifactStagingDirectory)'
180180

181+
- job: test_deno
182+
displayName: "Build and test the deno example"
183+
steps:
184+
- template: ci/azure-install-rust.yml
185+
- script: rustup target add wasm32-unknown-unknown
186+
displayName: "install wasm target"
187+
- template: ci/azure-install-deno.yml
188+
- script: cd examples/deno && ./build.sh && $HOME/.deno/bin/deno run --allow-read test.ts
189+
displayName: "build and run deno example"
190+
181191
- job: build_raytrace
182192
displayName: "Build raytrace examples"
183193
steps:

ci/azure-install-deno.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
steps:
2+
- script: curl -fsSL https://deno.land/x/install/install.sh | sh
3+
displayName: "install deno"

crates/cli-support/src/js/mod.rs

Lines changed: 100 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ impl<'a> Context<'a> {
143143
| OutputMode::Node {
144144
experimental_modules: true,
145145
}
146-
| OutputMode::Web => {
146+
| OutputMode::Web
147+
| OutputMode::Deno => {
147148
if contents.starts_with("function") {
148149
let body = &contents[8..];
149150
if export_name == definition_name {
@@ -269,6 +270,63 @@ impl<'a> Context<'a> {
269270
reset_indentation(&shim)
270271
}
271272

273+
// generates somthing like
274+
// ```js
275+
// import * as import0 from './snippets/.../inline1.js';
276+
// ```,
277+
//
278+
// ```js
279+
// const imports = {
280+
// __wbindgen_placeholder__: {
281+
// __wbindgen_throw: function(..) { .. },
282+
// ..
283+
// },
284+
// './snippets/deno-65e2634a84cc3c14/inline1.js': import0,
285+
// }
286+
// ```
287+
fn generate_deno_imports(&self) -> (String, String) {
288+
let mut imports = String::new();
289+
let mut wasm_import_object = "const imports = {\n".to_string();
290+
291+
wasm_import_object.push_str(&format!(" {}: {{\n", crate::PLACEHOLDER_MODULE));
292+
293+
for (id, js) in crate::sorted_iter(&self.wasm_import_definitions) {
294+
let import = self.module.imports.get(*id);
295+
wasm_import_object.push_str(&format!("{}: {},\n", &import.name, js.trim()));
296+
}
297+
298+
wasm_import_object.push_str("\t},\n");
299+
300+
// e.g. snippets without parameters
301+
let import_modules = self
302+
.module
303+
.imports
304+
.iter()
305+
.map(|import| &import.module)
306+
.filter(|module| module.as_str() != PLACEHOLDER_MODULE);
307+
for (i, module) in import_modules.enumerate() {
308+
imports.push_str(&format!("import * as import{} from '{}'\n", i, module));
309+
wasm_import_object.push_str(&format!(" '{}': import{},", module, i))
310+
}
311+
312+
wasm_import_object.push_str("\n};\n\n");
313+
314+
(imports, wasm_import_object)
315+
}
316+
317+
fn generate_deno_wasm_loading(&self, module_name: &str) -> String {
318+
// Deno removed support for .wasm imports in https://github.com/denoland/deno/pull/5135
319+
// the issue for bringing it back is https://github.com/denoland/deno/issues/5609.
320+
format!(
321+
"const file = new URL(import.meta.url).pathname;
322+
const wasmFile = file.substring(0, file.lastIndexOf(Deno.build.os === 'windows' ? '\\\\' : '/') + 1) + '{}_bg.wasm';
323+
const wasmModule = new WebAssembly.Module(Deno.readFileSync(wasmFile));
324+
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
325+
const wasm = wasmInstance.exports;",
326+
module_name
327+
)
328+
}
329+
272330
/// Performs the task of actually generating the final JS module, be it
273331
/// `--target no-modules`, `--target web`, or for bundlers. This is the very
274332
/// last step performed in `finalize`.
@@ -331,6 +389,18 @@ impl<'a> Context<'a> {
331389
}
332390
}
333391

392+
OutputMode::Deno => {
393+
let (js_imports, wasm_import_object) = self.generate_deno_imports();
394+
imports.push_str(&js_imports);
395+
footer.push_str(&wasm_import_object);
396+
397+
footer.push_str(&self.generate_deno_wasm_loading(module_name));
398+
399+
if needs_manual_start {
400+
footer.push_str("wasm.__wbindgen_start();\n");
401+
}
402+
}
403+
334404
// With Bundlers and modern ES6 support in Node we can simply import
335405
// the wasm file as if it were an ES module and let the
336406
// bundler/runtime take care of it.
@@ -443,7 +513,8 @@ impl<'a> Context<'a> {
443513
| OutputMode::Node {
444514
experimental_modules: true,
445515
}
446-
| OutputMode::Web => {
516+
| OutputMode::Web
517+
| OutputMode::Deno => {
447518
for (module, items) in crate::sorted_iter(&self.js_imports) {
448519
imports.push_str("import { ");
449520
for (i, (item, rename)) in items.iter().enumerate() {
@@ -1238,27 +1309,36 @@ impl<'a> Context<'a> {
12381309
}
12391310

12401311
fn expose_text_processor(&mut self, s: &str, args: &str) -> Result<(), Error> {
1241-
if self.config.mode.nodejs() {
1242-
let name = self.import_name(&JsImport {
1243-
name: JsImportName::Module {
1244-
module: "util".to_string(),
1245-
name: s.to_string(),
1246-
},
1247-
fields: Vec::new(),
1248-
})?;
1249-
self.global(&format!("let cached{} = new {}{};", s, name, args));
1250-
} else if !self.config.mode.always_run_in_browser() {
1251-
self.global(&format!(
1252-
"
1312+
match &self.config.mode {
1313+
OutputMode::Node { .. } => {
1314+
let name = self.import_name(&JsImport {
1315+
name: JsImportName::Module {
1316+
module: "util".to_string(),
1317+
name: s.to_string(),
1318+
},
1319+
fields: Vec::new(),
1320+
})?;
1321+
self.global(&format!("let cached{} = new {}{};", s, name, args));
1322+
}
1323+
OutputMode::Bundler {
1324+
browser_only: false,
1325+
} => {
1326+
self.global(&format!(
1327+
"
12531328
const l{0} = typeof {0} === 'undefined' ? \
12541329
(0, module.require)('util').{0} : {0};\
12551330
",
1256-
s
1257-
));
1258-
self.global(&format!("let cached{0} = new l{0}{1};", s, args));
1259-
} else {
1260-
self.global(&format!("let cached{0} = new {0}{1};", s, args));
1261-
}
1331+
s
1332+
));
1333+
self.global(&format!("let cached{0} = new l{0}{1};", s, args));
1334+
}
1335+
OutputMode::Deno
1336+
| OutputMode::Web
1337+
| OutputMode::NoModules { .. }
1338+
| OutputMode::Bundler { browser_only: true } => {
1339+
self.global(&format!("let cached{0} = new {0}{1};", s, args))
1340+
}
1341+
};
12621342

12631343
Ok(())
12641344
}

crates/cli-support/src/lib.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ enum OutputMode {
7474
Web,
7575
NoModules { global: String },
7676
Node { experimental_modules: bool },
77+
Deno,
7778
}
7879

7980
enum Input {
@@ -210,6 +211,14 @@ impl Bindgen {
210211
Ok(self)
211212
}
212213

214+
pub fn deno(&mut self, deno: bool) -> Result<&mut Bindgen, Error> {
215+
if deno {
216+
self.switch_mode(OutputMode::Deno, "--target deno")?;
217+
self.encode_into(EncodeInto::Always);
218+
}
219+
Ok(self)
220+
}
221+
213222
pub fn no_modules_global(&mut self, name: &str) -> Result<&mut Bindgen, Error> {
214223
match &mut self.mode {
215224
OutputMode::NoModules { global } => *global = name.to_string(),
@@ -509,7 +518,8 @@ impl OutputMode {
509518
| OutputMode::Web
510519
| OutputMode::Node {
511520
experimental_modules: true,
512-
} => true,
521+
}
522+
| OutputMode::Deno => true,
513523
_ => false,
514524
}
515525
}
@@ -537,15 +547,6 @@ impl OutputMode {
537547
}
538548
}
539549

540-
fn always_run_in_browser(&self) -> bool {
541-
match self {
542-
OutputMode::Web => true,
543-
OutputMode::NoModules { .. } => true,
544-
OutputMode::Bundler { browser_only } => *browser_only,
545-
_ => false,
546-
}
547-
}
548-
549550
fn web(&self) -> bool {
550551
match self {
551552
OutputMode::Web => true,
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use std::ffi::OsString;
2+
use std::fs;
3+
use std::path::Path;
4+
use std::process::Command;
5+
6+
use anyhow::{Context, Error};
7+
8+
use crate::node::{exec, SHARED_SETUP};
9+
10+
pub fn execute(
11+
module: &str,
12+
tmpdir: &Path,
13+
args: &[OsString],
14+
tests: &[String],
15+
) -> Result<(), Error> {
16+
let mut js_to_execute = format!(
17+
r#"import * as wasm from "./{0}.js";
18+
19+
{console_override}
20+
21+
// global.__wbg_test_invoke = f => f();
22+
23+
// Forward runtime arguments. These arguments are also arguments to the
24+
// `wasm-bindgen-test-runner` which forwards them to deno which we
25+
// forward to the test harness. this is basically only used for test
26+
// filters for now.
27+
cx.args(Deno.args.slice(1));
28+
29+
const ok = await cx.run(tests.map(n => wasm.__wasm[n]));
30+
if (!ok) Deno.exit(1);
31+
32+
const tests = [];
33+
"#,
34+
module,
35+
console_override = SHARED_SETUP,
36+
);
37+
38+
for test in tests {
39+
js_to_execute.push_str(&format!("tests.push('{}')\n", test));
40+
}
41+
42+
let js_path = tmpdir.join("run.js");
43+
fs::write(&js_path, js_to_execute).context("failed to write JS file")?;
44+
45+
/*
46+
// Augment `NODE_PATH` so things like `require("tests/my-custom.js")` work
47+
// and Rust code can import from custom JS shims. This is a bit of a hack
48+
// and should probably be removed at some point.
49+
let path = env::var("NODE_PATH").unwrap_or_default();
50+
let mut path = env::split_paths(&path).collect::<Vec<_>>();
51+
path.push(env::current_dir().unwrap());
52+
path.push(tmpdir.to_path_buf());
53+
let extra_node_args = env::var("NODE_ARGS")
54+
.unwrap_or_default()
55+
.split(",")
56+
.map(|s| s.to_string())
57+
.filter(|s| !s.is_empty())
58+
.collect::<Vec<_>>();
59+
exec(
60+
Command::new("node")
61+
.env("NODE_PATH", env::join_paths(&path).unwrap())
62+
.args(&extra_node_args)
63+
.arg(&js_path)
64+
.args(args),
65+
)*/
66+
exec(
67+
Command::new("deno")
68+
.arg("run")
69+
.arg("--allow-read")
70+
.arg(&js_path)
71+
.args(args),
72+
)
73+
}

0 commit comments

Comments
 (0)