Search code examples
javascript.netvitewebassembly.net-7.0

How do I use vite with the "new" .net 7 web assembly project


I am building an client-side application (using html/javascript) and would like to use a web assembly in it, compiled with .net 7.

https://learn.microsoft.com/en-us/aspnet/core/client-side/dotnet-interop

https://www.meziantou.net/using-dotnet-code-from-javascript-using-webassembly.htm

The application is NOT using blazor, it is a normal React vite-based application. So from the dotent, I get this dotnet.js file and a bunch of other files (the AppBundle folder), and now I would like to use that from some React component in my Vite application. So how do I do that?

If I try to go directly like this:

import { dotnet } = from '../../wasm/bin/Release/net7.0/browser-wasm/AppBundle/dotnet.js';

// then use use dotent

Then it even works in debug, but when bundling, vite cries about dynamic imports, and cannot pack the AppBundle properly, leaving the .dlls and other stuff behind. Also, dotent.js does not like being renamed to something like dotnet.123ak.js, and just breaks. Also, vite renames dotnet.wasm to dotnet.123ak.wasm, maybe this is what the dotnet part does not like.

Anyway. It would be enough to have sort of a "virtual" (unprocessed) import which I can somehow specify to resolve to the "dotent.js" file forcefully to some fixed path (external). How can I achieve this with vite? I.e. basically, I want

  • my project to compile, i.e not give errors on compilation.
  • the output file (say, page.tsx => page.js) to refer to some specific fixed path, like '/AppBundle/dotnet.js'. As an example:

If I import like this:

import { dotnet } from 'some-file'

Then on build I do not get any errors, and after build I get the some-file replaced with /MyAppBundle/dotnet.js (a fixed value in config)

How can I achive this (for example, with webpack, there is "resolve" option in config, maybe there is something similar to help in vite?)


Solution

  • Figured it out. You can use await import(...) to make it work. Have written a post regarding this:

    https://medium.com/@nbelyh/using-net-as-a-webassembly-from-javascript-react-16fd9373c411

    usage

      const { dotnet, loading } = 
         useDotNet('/path/to/your/AppBundle/dotnet.js')
    

    definition

    import { useEffect, useRef, useState } from 'react';
    
    export const useDotNet = (url: string) => {
    
      const dotnetUrl = useRef('');
      const [dotnet, setDotNet] = useState<any>(null);
      const [loading, setLoading] = useState(true);
    
      const load = async (currentUrl: string): Promise<any> => {
    
        const module = await import(/* @vite-ignore */ currentUrl);
    
        const { getAssemblyExports, getConfig } = await module
          .dotnet
          .withDiagnosticTracing(false)
          .create();
    
        const config = getConfig();
        const exports = await getAssemblyExports(config.mainAssemblyName);
        return exports;
      }
    
      useEffect(() => {
        if (dotnetUrl.current !== url) { // safeguard to prevent double-loading
          setLoading(true);
          dotnetUrl.current = url;
          load(url)
            .then(exports => setDotNet(exports))
            .finally(() => setLoading(false))
        }
      }, [url]);
      return { dotnet, loading };
    }