only-cliches / cpp-wasm-loader

C/C++ to WASM Webpack Loader

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

typescript functionality

dugagjin opened this issue · comments

Typescript functionality

First, thanks for your awesome loader.

Problem

I would like to be able to use it in typescript but as you could guess this gives an error:

import wasm from './source.c';

The error is: Cannot find module './source.c' because it expects a .js or .ts file with an export statement.

Do you have any idea of how to make this work?

What I have tried:

source.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <emscripten/emscripten.h>

int main(int argc, char ** argv) {
    printf("WASM loaded\n");
}

int EMSCRIPTEN_KEEPALIVE roll_dice() {
    srand(time(NULL));
    return rand() % 6 + 1;
}

wasm.js:

export * from './source.c';

typings.d.ts:

declare module '*'

main.ts:

import * as wasm from './wasm';

wasm.initialize().then((module: any) => {
    const result = module._roll_dice();
    console.log(result);
});

Results

Webpack compiles everything with no errors but when I open my HTML page on a localhost those are the errors that I get:

wasm streaming compile failed: TypeError: Response has unsupported MIME type
source.c:15:14913
falling back to ArrayBuffer instantiation
source.c:15:14974
on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)
source.c:15:21983
on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)
source.c:15:22002
failed to asynchronously prepare wasm: abort("on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)"). Build with -s ASSERTIONS=1 for more info.
source.c:15:14547
abort("on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)"). Build with -s ASSERTIONS=1 for more info.
source.c:15:21983
abort("on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)"). Build with -s ASSERTIONS=1 for more info.
source.c:15:22002
uncaught exception: abort("abort(\"on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)\"). Build with -s ASSERTIONS=1 for more info."). Build with -s ASSERTIONS=1 for more info.

Thanks for your time.

Hey, have you tried declare module '*.c'; in the .d.ts file? That should allow TS to import the file.

Webpack compiles without errors when using declare module '*.c' in my d.ts file.

I call the wasm code using import wasm from 'source.c'. in my TS file.

But when opening the HTML page on localhost I have:

Uncaught TypeError: Cannot read property 'initialize' of undefined

Basically, wasm is undefined.

Do you have Emscripten installed and did you set the proper path to it in webpack config? Can you compile the source.c using Emscripten?

I am on Windows, emscripten is installed and the path refers to the emcc.bat file C:/emsdk/emscripten/1.37.33/emcc.bat

I can compile the source.c using emscripten. For example, emcc source.c -s WASM=1 -o source.html works and the html file displays what is written in source.c

Ok, what's your webpack version? I'll try it and see if I can find some solution.

I have webpack version 3.10.0

Many thanks for helping me.

I got it working by using "module": "es2015" in compilerOptions in tsconfig.json. Is that an acceptable solution for you?

I have to target es2017. The most that I can do is "module": "es2015", "target": "es2017".

Sadly it didn't make it work.

But I found out that my index.html is searching for index.wasm file. I looked where index.wasm is called in the bundle.js generated by webpack and I found this function:

function integrateWasmJS() {
    var wasmTextFile = "index.wast";
    var wasmBinaryFile = "index.wasm";
    var asmjsCodeFile = "index.temp.asm.js";
    
    // more code underneath but not relevant.

Here if change index.wasm with ./dist/source-d2a469dda977d3cf75cbba1901d46a81.wasm (where my actual wasm file is located relative to the html file) then everything works. 👍

The name index.wasm comes from your code index.js:

var inputFile = `input${path.extname(this.resourcePath)}`;
var indexFile = 'index.js';
var wasmFile = 'index.wasm';

If I change "index" to, for example, "test" for the above code then integrateWasmJS function will also have "test" instead of "index".

function integrateWasmJS() {
    var wasmTextFile = "test.wast";
    var wasmBinaryFile = "test.wasm";
    var asmjsCodeFile = "test.temp.asm.js";
    
    // more code underneath but not relevant.

Why this happens:

If you compile C/C++ to webassembly using the command emcc source.c -s WASM=1 -o source.js you can see that the produced javascript file contains the same integrateWasmJS function. Furthermore, the values of the variables wasmTextFile, wasmBinaryFile and asmjsCodeFile inside that function are determined by the name that is passed as an argument to emcc (in this case "source").

How I managed to get it work

I modified your code in order to have:

var wasmBuildName = createBuildWasmName(this.resourcePath, content);

var inputFile = `input${path.extname(this.resourcePath)}`;
var indexFile = wasmBuildName.replace('.wasm', '.js');
var wasmFile = wasmBuildName;

Now integrateWasmJS function produced by emcc will refer to the correct wasm file and not index.wasm. Otherwise, for every C/C++ file, it referred to the same index.wasm.

I moved my index.html inside the same folder where my bundle.js and .wasm code is.

The problem is that I did not found out how to do for example this:

function integrateWasmJS() {
    var wasmTextFile = "./dist/test.wast";
    var wasmBinaryFile = "./dist/test.wasm";
    var asmjsCodeFile = "./dist/test.temp.asm.js";
    
    // more code underneath but not relevant.

I am still searching for how to do this since it will enable me to have my index.html wherever I want. Until then this method resolved my problem and everything works! 🎊

Thanks ! 😄

Wow, thanks for such a detailed description :-)

As you probably guessed, this loader is a brittle proof-of-concept rather than a production-ready library. I'm sure that Webpack supports the scenario with extracting the imported files to a different folder, but I'm not sure how it should be done.

It's pretty much the same situation as with extracting all CSS to a single file at the root of your app and then importing it in index.html.

I may look at it in the coming weeks.