rust-webpack-template icon indicating copy to clipboard operation
rust-webpack-template copied to clipboard

Typescript example

Open dakom opened this issue 7 years ago • 6 comments

Seems that ts-loader and/or awesome-typescript-loader don't play nicely with the async->sync import solution bindgen produces.

This was already raised and closed here: https://github.com/rustwasm/wasm-bindgen/issues/700 but it might be helpful to have a Typescript boilerplate as a reference someday :)

dakom avatar Sep 20 '18 22:09 dakom

TL;DR: The magic tricks are:

  • "module": "esNext" in tsconfig.json
  • await import('./mymod')
  • typeof import('./mymod')

Details

In your tsconfig.json, set "module": "esNext" so that you can use async import(...):

{
  "compilerOptions": {
    "target": "es5",
    "module": "esNext",
    "lib": ["dom", "es2015"],
    "strict": true,  
    "esModuleInterop": true
  }
}

In your webpack.config.js, enable ts-loader (as per the usual instructions) and the experimental built-in *.wasm loader:

  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      },
      {
        test: /\.wasm$/,
        type: "webassembly/experimental"
      }
    ]
  },
  resolve: {
    extensions: [ '.tsx', '.ts', '.js', '.wasm' ]
  },

Finally, let's assume you have the following files generated by wasm-bindgen:

  • mymod.js
  • mymod.d.ts
  • mymod_bg.wasm

Then you can use a structure like this in your top-level *.ts file:

function start(mymod: typeof import('./mymod')) {
    console.log("All modules loaded");
    mymod.my_exported_rust_function();
}

async function load() {
    start(await import('./mymod'));
}

load();

The magic trick here is typeof import('./mymod'), which allows you declare the type of the asynchronously-loaded module.

I'd be happy to try to turn this into a fully-worked test case that can run on CI and that can be used as example, but it might need to wait a few days until I have time.

emk avatar Oct 03 '18 11:10 emk

Thanks @emk - that does seem to work!

Though now Typescript is having trouble loading some other modules... any clue how to fix that? Here's some specific examples:

import {mat4} from "gl-matrix";
import WebFont from "webfontloader";

In both cases typescript claims it can't find the modules, though they are npm-installed

dakom avatar Oct 09 '18 15:10 dakom

Though now Typescript is having trouble loading some other modules... any clue how to fix that?

A lot depends on whether your TypeScript setup was working correctly before applying this workaround, I'm sad to say. :-/ It might be missing *.d.ts modules, bad lookup paths, wrong magic incantations, etc. Try removing the await import and see if you can get the rest working on its own.

emk avatar Oct 09 '18 18:10 emk

Ah - moduleResolution: node seemed to do the trick :)

dakom avatar Oct 10 '18 07:10 dakom

wasm-pack-plugin version 0.1.0 was recently released and among other improvements, it now has the option to configure wasm-pack to generate TypeScript bindings that can be used in your code.

Here's an example webpack configuration to make use of it:

webpack.config.js

const path = require("path");
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./index.ts",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js"
  },
  plugins: [
    new HtmlWebpackPlugin(),
    new WasmPackPlugin({
      crateDirectory: path.resolve(__dirname, "."),
      withTypeScript: true // this is new
    }),
  ],
  resolve: {
    extensions: [".ts", ".tsx", ".js", ".wasm"]
  },
  module: {
    rules: [{
      test: /\.tsx?$/,
      loader: "ts-loader"
    }]
  }
};

tsconfig.json

{
    "compilerOptions": {
        "module": "esnext", // needed to have dynamic imports
        "lib": [
            "dom",
            "es2015"
        ]
    },
    "files": [
        "./index.ts"
    ]
}

example index.ts

import("./pkg/yourlib").then(module => {
  // won't typecheck if yourlib does not expose the run function
  module.run();
});

danieledapo avatar Oct 12 '18 19:10 danieledapo

I have a variant on this that I haven't quite puzzled my way through, and thats exporting a typescript package that is using wasm-bindgen as an accelerator for just some parts of it. Doing the async import dance around the entry points for browser bundles is easy, but I'm having some head scratching getting there - it seems like webpack still wants an await around that, even though as a library I would have expected that the async dance could happen upstream, by whomever is using the published package.

rbtcollins avatar Oct 10 '19 21:10 rbtcollins