Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating build process to create .d.ts declaration files #256

Merged
merged 7 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- name: Install Rust
run: rustup update stable --no-self-update && rustup default stable
- name: Install wasm32-unknown-unknown target
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ package-lock.json
/test/output
/jco.sh
/docs/book
/src/**/*.d.ts
/src/**/*.d.ts.map
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Options include:
* `--instantiation [mode]`: Instead of a direct ES module, export an `instantiate` function which can take the imports as an argument instead of implicit imports. The `instantiate` function can be async (with `--instantiation` or `--instantiation async`), or sync (with `--instantiation sync`).
* `--valid-lifting-optimization`: Internal validations are removed assuming that core Wasm binaries are valid components, providing a minor output size saving.
* `--tracing`: Emit tracing calls for all function entry and exits.
* `--no-namespaced-exports`: Removes exports of the type `test as "test:flavorful/test"` which are not compatible with typescript

#### Bindgen Crate

Expand Down
2 changes: 2 additions & 0 deletions crates/js-component-bindgen-component/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl Guest for JsComponentBindgenComponent {
.unwrap_or(options.compat.unwrap_or(false)),
valid_lifting_optimization: options.valid_lifting_optimization.unwrap_or(false),
tracing: options.tracing.unwrap_or(false),
no_namespaced_exports: options.no_namespaced_exports.unwrap_or(false),
};

let js_component_bindgen::Transpiled {
Expand Down Expand Up @@ -131,6 +132,7 @@ impl Guest for JsComponentBindgenComponent {
valid_lifting_optimization: false,
base64_cutoff: 0,
tracing: false,
no_namespaced_exports: false,
};

let files = generate_types(name, resolve, world, opts).map_err(|e| e.to_string())?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ world js-component-bindgen {

/// Whether or not to emit `tracing` calls on function entry/exit.
tracing: option<bool>,

/// Whether to generate namespaced exports like `foo as "local:package/foo"`.
/// These exports can break typescript builds.
no-namespaced-exports: option<bool>,
}

variant wit {
Expand Down
23 changes: 16 additions & 7 deletions crates/js-component-bindgen/src/esm_bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use heck::ToLowerCamelCase;

use crate::names::{maybe_quote_id, maybe_quote_member, LocalNames};
use crate::source::Source;
use crate::{uwrite, uwriteln};
use crate::{uwrite, uwriteln, TranspileOpts};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Write;

Expand Down Expand Up @@ -131,6 +131,7 @@ impl EsmBindgen {
output: &mut Source,
instantiation: bool,
local_names: &mut LocalNames,
opts: &TranspileOpts,
) {
if self.exports.is_empty() {
if instantiation {
Expand Down Expand Up @@ -165,8 +166,6 @@ impl EsmBindgen {
for (alias, export_name) in &self.export_aliases {
if first {
first = false
} else {
uwrite!(output, ", ");
}
let local_name = match &self.exports[export_name] {
Binding::Local(local_name) => local_name,
Expand All @@ -175,17 +174,18 @@ impl EsmBindgen {
let alias_maybe_quoted = maybe_quote_id(alias);
if local_name == alias_maybe_quoted {
output.push_str(local_name);
uwrite!(output, ", ");
} else if instantiation {
uwrite!(output, "{alias_maybe_quoted}: {local_name}");
} else {
uwrite!(output, ", ");
} else if !self.contains_js_quote(&alias_maybe_quoted) || !opts.no_namespaced_exports {
uwrite!(output, "{local_name} as {alias_maybe_quoted}");
uwrite!(output, ", ");
}
}
for (export_name, export) in &self.exports {
if first {
first = false
} else {
uwrite!(output, ", ");
}
let local_name = match export {
Binding::Local(local_name) => local_name,
Expand All @@ -194,15 +194,24 @@ impl EsmBindgen {
let export_name_maybe_quoted = maybe_quote_id(export_name);
if local_name == export_name_maybe_quoted {
output.push_str(local_name);
uwrite!(output, ", ");
} else if instantiation {
uwrite!(output, "{export_name_maybe_quoted}: {local_name}");
} else {
uwrite!(output, ", ");
} else if !self.contains_js_quote(&export_name_maybe_quoted)
|| !opts.no_namespaced_exports
{
uwrite!(output, "{local_name} as {export_name_maybe_quoted}");
uwrite!(output, ", ");
}
}
uwrite!(output, " }}");
}

fn contains_js_quote(&self, js_string: &String) -> bool {
js_string.contains("\"") || js_string.contains("'") || js_string.contains("`")
}

fn binding_has_used(&self, binding: &Binding) -> bool {
match binding {
Binding::Interface(iface) => iface
Expand Down
9 changes: 7 additions & 2 deletions crates/js-component-bindgen/src/transpile_bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ pub struct TranspileOpts {
pub valid_lifting_optimization: bool,
/// Whether or not to emit `tracing` calls on function entry/exit.
pub tracing: bool,
/// Whether to generate namespaced exports like `foo as "local:package/foo"`.
/// These exports can break typescript builds.
pub no_namespaced_exports: bool,
}

#[derive(Default, Clone, Debug)]
Expand Down Expand Up @@ -152,7 +155,7 @@ pub fn transpile_bindgen(
instantiator.gen.src.js(&instantiator.src.js);
instantiator.gen.src.js_init(&instantiator.src.js_init);

instantiator.gen.finish_component(name, files);
instantiator.gen.finish_component(name, files, &opts);

let exports = instantiator
.gen
Expand All @@ -173,7 +176,7 @@ pub fn transpile_bindgen(
}

impl<'a> JsBindgen<'a> {
fn finish_component(&mut self, name: &str, files: &mut Files) {
fn finish_component(&mut self, name: &str, files: &mut Files, opts: &TranspileOpts) {
let mut output = source::Source::default();
let mut compilation_promises = source::Source::default();

Expand Down Expand Up @@ -262,6 +265,7 @@ impl<'a> JsBindgen<'a> {
&mut self.src.js,
self.opts.instantiation.is_some(),
&mut self.local_names,
opts,
);
uwrite!(
output,
Expand Down Expand Up @@ -312,6 +316,7 @@ impl<'a> JsBindgen<'a> {
&mut output,
self.opts.instantiation.is_some(),
&mut self.local_names,
opts,
);
}

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
},
"homepage": "https://github.com/bytecodealliance/jco#readme",
"scripts": {
"build": "cargo xtask build workspace",
"build": "cargo xtask build workspace && npm run build:typescript",
"build:typescript": "tsc -p tsconfig.json",
"build:types:preview2-shim": "cargo xtask generate wasi-types",
"lint": "eslint -c eslintrc.cjs lib/**/*.js packages/*/lib/**/*.js",
"test": "mocha -u tdd test/test.js --timeout 120000"
Expand Down
4 changes: 3 additions & 1 deletion src/cmd/transpile.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ async function wasm2Js (source) {
* js?: bool,
* minify?: bool,
* optimize?: bool,
* noNamespacedExports?: bool,
* optArgs?: string[],
* }} opts
* @returns {Promise<{ files: { [filename: string]: Uint8Array }, imports: string[], exports: [string, 'function' | 'instance'][] }>}
Expand Down Expand Up @@ -120,7 +121,8 @@ export async function transpileComponent (component, opts = {}) {
noNodejsCompat: !(opts.nodejsCompat ?? true),
noTypescript: opts.noTypescript || false,
tlaCompat: opts.tlaCompat ?? false,
base64Cutoff: opts.js ? 0 : opts.base64Cutoff ?? 5000
base64Cutoff: opts.js ? 0 : opts.base64Cutoff ?? 5000,
noNamespacedExports: !opts.namespacedExports,
});

let outDir = (opts.outDir ?? '').replace(/\\/g, '/');
Expand Down
1 change: 1 addition & 0 deletions src/jco.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ program.command('transpile')
.option('--js', 'output JS instead of core WebAssembly')
.addOption(new Option('-I, --instantiation [mode]', 'output for custom module instantiation').choices(['async', 'sync']).preset('async'))
.option('-q, --quiet', 'disable logging')
.option('--no-namespaced-exports', 'disable namespaced exports for typescript compatibility')
.option('--', 'for --optimize, custom wasm-opt arguments (defaults to best size optimization)')
.action(asyncAction(transpile));

Expand Down
26 changes: 25 additions & 1 deletion test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { deepStrictEqual, ok, strictEqual } from 'node:assert';
import { mkdir, readFile, rm, symlink, writeFile, mkdtemp } from 'node:fs/promises';
import { fileURLToPath, pathToFileURL } from 'url';
import { exec, jcoPath } from './helpers.js';
import { tmpdir } from 'node:os';
import { tmpdir, EOL } from 'node:os';
import { resolve, normalize, sep } from 'node:path';

export async function cliTest (fixtures) {
Expand Down Expand Up @@ -89,6 +89,30 @@ export async function cliTest (fixtures) {
ok(source.includes('export const $init'));
});

test('Transpile without namespaced exports', async () => {
const name = 'flavorful';
const { stderr } = await exec(jcoPath, 'transpile', `test/fixtures/components/${name}.component.wasm`, '--no-namespaced-exports', '--no-wasi-shim', '--name', name, '-o', outDir);
strictEqual(stderr, '');
const source = await readFile(`${outDir}/${name}.js`);
const finalLine = source.toString().split("\n").at(-1)
//Check final line is the export statement
ok(finalLine.toString().includes("export {"));
//Check that it does not contain the namespaced export
ok(!finalLine.toString().includes("test:flavorful/test"));
});

test('Transpile with namespaced exports', async () => {
const name = 'flavorful';
const { stderr } = await exec(jcoPath, 'transpile', `test/fixtures/components/${name}.component.wasm`, '--no-wasi-shim', '--name', name, '-o', outDir);
strictEqual(stderr, '');
const source = await readFile(`${outDir}/${name}.js`);
const finalLine = source.toString().split("\n").at(-1)
//Check final line is the export statement
ok(finalLine.toString().includes("export {"));
//Check that it does contain the namespaced export
ok(finalLine.toString().includes("test as 'test:flavorful/test'"));
});

test('Optimize', async () => {
const component = await readFile(`test/fixtures/components/flavorful.component.wasm`);
const { stderr, stdout } = await exec(jcoPath, 'opt', `test/fixtures/components/flavorful.component.wasm`, '-o', outFile);
Expand Down
11 changes: 11 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"include": ["src/**/*"],
"exclude": ["node_modules", "src/cmd/**", "src/*.d.ts"],
"compilerOptions": {
"allowJs": true,
"declaration": true,
"emitDeclarationOnly": true,
"declarationMap": true,
"skipLibCheck": true,
}
}
5 changes: 2 additions & 3 deletions xtask/src/build/jco.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use anyhow::Context;
use anyhow::{Context, Result};
use std::{collections::HashMap, fs, io::Write, path::PathBuf};

use anyhow::Result;
use wit_component::ComponentEncoder;

pub(crate) fn run() -> Result<()> {
Expand Down Expand Up @@ -67,6 +65,7 @@ fn transpile(component_path: &str, name: String) -> Result<()> {
tla_compat: true,
valid_lifting_optimization: false,
tracing: false,
no_namespaced_exports: true,
};

let transpiled = js_component_bindgen::transpile(&adapted_component, opts)?;
Expand Down
1 change: 1 addition & 0 deletions xtask/src/generate/wasi_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub(crate) fn run() -> Result<()> {
valid_lifting_optimization: false,
base64_cutoff: 0,
tracing: false,
no_namespaced_exports: true,
};

let files = generate_types(name, resolve, world, opts)?;
Expand Down