[wasm] Webpack & NextJS & TypeScript samples (#63335)

- nextJs sample
- webpack sample
- typescript sample
Co-authored-by: campersau <buchholz.bastian@googlemail.com>
This commit is contained in:
Pavel Savara 2022-01-09 20:24:47 +01:00 committed by GitHub
parent ce2165d808
commit 3591addba0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 12704 additions and 15 deletions

1
.gitignore vendored
View File

@ -187,6 +187,7 @@ node_modules/
*.metaproj.tmp
bin.localpkg/
src/mono/wasm/runtime/dotnet.d.ts.sha256
src/mono/sample/wasm/browser-nextjs/public/
# RIA/Silverlight projects
Generated_Code/

View File

@ -13,6 +13,13 @@
<TestTrimming Condition="'$(TestTrimming)' == ''">false</TestTrimming>
</PropertyGroup>
<!-- Samples which are too complex for CI -->
<ItemGroup Condition="'$(TargetOS)' == 'Browser'">
<ProjectExclusions Include="$(MonoProjectRoot)sample\wasm\console-node-ts\Wasm.Console.Node.TS.Sample.csproj" />
<ProjectExclusions Include="$(MonoProjectRoot)sample\wasm\browser-webpack\Wasm.Browser.WebPack.Sample.csproj" />
<ProjectExclusions Include="$(MonoProjectRoot)sample\wasm\browser-nextjs\Wasm.Browser.NextJs.Sample.csproj" />
</ItemGroup>
<!-- Wasm aot on !windows -->
<ItemGroup Condition="'$(TargetOS)' == 'Browser' and '$(BuildAOTTestsOnHelix)' == 'true' and '$(RunDisabledWasmTests)' != 'true' and '$(RunAOTCompilation)' == 'true' and '$(BrowserHost)' != 'Windows'">
<!-- Exceeds VM resources in CI on compilation: https://github.com/dotnet/runtime/issues/61339 -->

View File

@ -0,0 +1,30 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

View File

@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.CompilerServices;
namespace Sample
{
public class Test
{
public static int Main()
{
return 42;
}
}
}

View File

@ -0,0 +1,7 @@
## Sample for React component in the NextJs app.
Shows how to create react component and re-use runtime instance, when the component is instantiated multiple times.
```
dotnet build /p:TargetOS=Browser /p:TargetArchitecture=wasm /p:Configuration=Debug /t:RunSample
```

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<WasmCopyAppZipToHelixTestDir>false</WasmCopyAppZipToHelixTestDir>
<WasmMainJSPath>package.json</WasmMainJSPath>
<WasmAppDir>public</WasmAppDir>
</PropertyGroup>
<Target Name="InstallNpmPackage" AfterTargets="WasmBuildApp" DependsOnTargets="Build">
<Exec Command="npm install" WorkingDirectory="$(MSBuildProjectDirectory)"/>
<Exec Command="npm install $(MicrosoftNetCoreAppRuntimePackNativeDir) --no-save" WorkingDirectory="$(MSBuildProjectDirectory)"/>
</Target>
<Target Name="RunSample" DependsOnTargets="Build">
<Exec Command="npm run dev" WorkingDirectory="$(MSBuildProjectDirectory)"/>
</Target>
</Project>

View File

@ -0,0 +1,52 @@
import { useState, useEffect } from 'react'
import createDotnetRuntime from '@microsoft/dotnet-runtime'
let dotnetRuntimePromise = undefined;
let meaningFunction = undefined;
async function createRuntime() {
try {
const response = await fetch('dotnet.wasm');
const arrayBuffer = await response.arrayBuffer();
return createDotnetRuntime({
configSrc: "./mono-config.json",
disableDotnet6Compatibility: true,
scriptDirectory: "/",
instantiateWasm: async (imports, successCallback) => {
try {
const arrayBufferResult = await WebAssembly.instantiate(arrayBuffer, imports);
successCallback(arrayBufferResult.instance);
} catch (err) {
console.error(err);
throw err;
}
}
});
} catch (err) {
console.error(err);
throw err;
}
}
async function dotnetMeaning() {
if (!dotnetRuntimePromise) {
dotnetRuntimePromise = createRuntime();
}
const { BINDING } = await dotnetRuntimePromise;
meaningFunction = BINDING.bind_static_method("[Wasm.Browser.NextJs.Sample] Sample.Test:Main");
return meaningFunction();
}
export default function DeepThought() {
const [meaning, setCount] = useState(undefined);
useEffect(async () => {
const meaning = await dotnetMeaning();
setCount(meaning);
}, []);
if (!meaning) {
return (<div>DeepThought is thinking ....</div>);
}
return (<div>Answer to the Ultimate Question of Life, the Universe, and Everything is : {meaning}!</div>);
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
{
"name": "browser-webpack",
"private": true,
"license": "MIT",
"scripts": {
"build:dotnet": "dotnet build /p:TargetOS=Browser /p:TargetArchitecture=wasm /p:Configuration=Debug",
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "11.1.3",
"react": "17.0.2",
"react-dom": "17.0.2"
}
}

View File

@ -0,0 +1,23 @@
import Head from 'next/head'
import DeepThought from '../components/deepThought'
export default function Home() {
return (
<div className="container">
<Head>
<title>NextJS dotnet sample</title>
</Head>
<main>
<h1 className="title">
Welcome to dotnet sample
</h1>
<DeepThought />
<DeepThought />
<DeepThought />
<DeepThought />
</main>
</div>
)
}

View File

@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.CompilerServices;
namespace Sample
{
public class Test
{
public static int Main()
{
return 42;
}
}
}

View File

@ -0,0 +1,5 @@
## Sample for packaging dotnet.js via WebPack
```
dotnet build /p:TargetOS=Browser /p:TargetArchitecture=wasm /p:Configuration=Debug /t:RunSample
```

View File

@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<WasmCopyAppZipToHelixTestDir>false</WasmCopyAppZipToHelixTestDir>
<WasmMainJSPath>main.js</WasmMainJSPath>
<!--
<WasmEnableES6>true</WasmEnableES6>
-->
</PropertyGroup>
<ItemGroup>
<WasmExtraFilesToDeploy Include="index.html" />
</ItemGroup>
<Target Name="CopyRelinkedPackage" AfterTargets="WasmBuildApp" DependsOnTargets="Build" Inputs="$(WasmAppDir)/dotnet.js;
$(WasmAppDir)/dotnet.wasm;
$(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet.d.ts;
$(MicrosoftNetCoreAppRuntimePackNativeDir)/package.json;" Outputs="bin/dotnet-runtime/.npm-stamp">
<ItemGroup>
<NpmPackageFiles Include="$(WasmAppDir)/dotnet.js"/>
<NpmPackageFiles Include="$(WasmAppDir)/dotnet.wasm"/>
<NpmPackageFiles Include="$(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet.d.ts"/>
<NpmPackageFiles Include="$(MicrosoftNetCoreAppRuntimePackNativeDir)/package.json"/>
</ItemGroup>
<Copy SourceFiles="@(NpmPackageFiles)" DestinationFolder="bin/dotnet-runtime" />
<Touch Files="bin/dotnet-runtime/.npm-stamp" AlwaysCreate="true" />
</Target>
<Target Name="WebPack" AfterTargets="WasmBuildApp" DependsOnTargets="CopyRelinkedPackage">
<Exec Command="npm install" WorkingDirectory="$(MSBuildProjectDirectory)"/>
<Exec Command="npm run webpack -- --env Configuration=$(Configuration) --env WasmAppDir=$(WasmAppDir)" WorkingDirectory="$(MSBuildProjectDirectory)"/>
</Target>
<Target Name="RunSample" DependsOnTargets="WebPack">
<Exec Command="dotnet serve --directory $(WasmAppDir)" WorkingDirectory="$(MSBuildProjectDirectory)"/>
</Target>
</Project>

View File

@ -0,0 +1,27 @@
import createDotnetRuntime from '@microsoft/dotnet-runtime'
import _ from 'underscore'
async function dotnetMeaning() {
try {
const { BINDING } = await createDotnetRuntime({
configSrc: "./mono-config.json",
scriptDirectory: "./",
});
const meaningFunction = BINDING.bind_static_method("[Wasm.Browser.WebPack.Sample] Sample.Test:Main");
return meaningFunction();
} catch (err) {
console.log(err)
throw err;
}
}
export async function main() {
const element = document.getElementById("out")
const ret = await dotnetMeaning();
const template = _.template('<%=ret%> as computed on dotnet');
element.textContent = template({ ret });
document.body.appendChild(element);
}

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Getting Started</title>
</head>
<body>
<h3 id="header">Wasm Browser ES6 Sample</h3>
Answer to the Ultimate Question of Life, the Universe, and Everything is : <span id="out"></span>
<script type="text/javascript">exports = {}</script>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="main.js"></script>
</body>
</html>

View File

@ -0,0 +1 @@
exports.app()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
{
"name": "browser-nextjs",
"private": true,
"license": "MIT",
"main": "index.js",
"scripts": {
"build": "dotnet build /p:TargetOS=Browser /p:TargetArchitecture=wasm /p:Configuration=Debug",
"webpack": "webpack"
},
"devDependencies": {
"webpack": "5.65.0",
"webpack-cli": "4.9.1"
},
"dependencies": {
"@microsoft/dotnet-runtime": "file:bin/dotnet-runtime",
"underscore": "1.13.2"
}
}

View File

@ -0,0 +1,15 @@
const path = require('path');
module.exports = (env) => {
const wasmAppDir = path.resolve(env.WasmAppDir ?? "bin/Debug/AppBundle");
const mode = env.Configuration === "Release" ? "production" : "development";
return {
mode,
entry: './app.js',
output: {
filename: 'app.js',
library: { type: "umd", name: "app", export: "main" },
path: wasmAppDir,
}
}
};

View File

@ -0,0 +1,17 @@
TOP=../../../../..
include ../wasm.mk
ifneq ($(AOT),)
override MSBUILD_ARGS+=/p:RunAOTCompilation=true
endif
ifneq ($(V),)
DOTNET_MONO_LOG_LEVEL=--setenv=MONO_LOG_LEVEL=debug
endif
PROJECT_NAME=Wasm.Console.Node.ES6.Sample.csproj
CONSOLE_DLL=Wasm.Console.Node.ES6.Sample.dll
MAIN_JS=main.mjs
run: run-console-node

View File

@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Threading.Tasks;
public class Test
{
public static async Task<int> Main(string[] args)
{
await Task.Delay(1);
Console.WriteLine("Hello World!");
return 42;
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<WasmCopyAppZipToHelixTestDir>false</WasmCopyAppZipToHelixTestDir>
<WasmMainJSPath>main.ts</WasmMainJSPath>
<!--
<WasmEnableES6>true</WasmEnableES6>
-->
</PropertyGroup>
<Target Name="TypeScript" AfterTargets="WasmBuildApp" DependsOnTargets="Build">
<Exec Command="npm install" WorkingDirectory="$(MSBuildProjectDirectory)"/>
<Exec Command="npm install $(MicrosoftNetCoreAppRuntimePackNativeDir) --no-save" WorkingDirectory="$(MSBuildProjectDirectory)"/>
<Exec Command="npm run tsc -- --outDir $(WasmAppDir)" WorkingDirectory="$(MSBuildProjectDirectory)"/>
</Target>
<Target Name="RunSample" DependsOnTargets="TypeScript">
<Exec Command="node main.js" IgnoreExitCode="true" WorkingDirectory="bin/$(Configuration)/AppBundle" />
</Target>
</Project>

View File

@ -0,0 +1,19 @@
import { createRequire } from 'module';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import createDotnetRuntime from '@microsoft/dotnet-runtime'
const { MONO } = await createDotnetRuntime(() => ({
imports: {
//TODO internalize into dotnet.js if possible
require: createRequire(import.meta.url)
},
//TODO internalize into dotnet.js if possible
scriptDirectory: dirname(fileURLToPath(import.meta.url)) + '/',
disableDotnet6Compatibility: true,
configSrc: "./mono-config.json",
}));
const app_args = process.argv.slice(2);
const dllName = "Wasm.Console.Node.TS.Sample.dll";
await MONO.mono_run_main_and_exit(dllName, app_args);

View File

@ -0,0 +1,60 @@
{
"name": "console-node-ts",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "console-node-ts",
"license": "MIT",
"devDependencies": {
"@types/node": "16.11.10",
"typescript": "4.5.2"
}
},
"../../../../../artifacts/bin/microsoft.netcore.app.runtime.browser-wasm/Debug/runtimes/browser-wasm/native": {
"name": "@microsoft/dotnet-runtime",
"version": "7.0.0-dev",
"extraneous": true,
"license": "MIT",
"devDependencies": {
"@rollup/plugin-typescript": "8.2.5",
"@typescript-eslint/eslint-plugin": "4.31.2",
"@typescript-eslint/parser": "4.31.2",
"eslint": "7.32.0",
"rollup": "2.56.3",
"rollup-plugin-consts": "1.0.2",
"rollup-plugin-dts": "4.0.0",
"rollup-plugin-terser": "7.0.2",
"tslib": "2.3.1",
"typescript": "4.4.3"
}
},
"node_modules/@types/node": {
"version": "16.11.10",
"dev": true,
"license": "MIT"
},
"node_modules/typescript": {
"version": "4.5.2",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
}
},
"dependencies": {
"@types/node": {
"version": "16.11.10",
"dev": true
},
"typescript": {
"version": "4.5.2",
"dev": true
}
}
}

View File

@ -0,0 +1,17 @@
{
"name": "console-node-ts",
"private": true,
"license": "MIT",
"description": "TypeScript sample for dotnet in WASM",
"type": "module",
"scripts": {
"build": "dotnet build /p:TargetOS=Browser /p:TargetArchitecture=wasm /p:Configuration=Debug",
"tsc": "tsc -p ."
},
"dependencies": {
},
"devDependencies": {
"@types/node": "16.11.10",
"typescript": "4.5.2"
}
}

View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"moduleResolution": "Node",
"target": "ESNext",
"module": "ESNext",
"strict": true,
"outDir": "bin"
}
}

View File

@ -1,7 +1,9 @@
//! Licensed to the .NET Foundation under one or more agreements.
//! The .NET Foundation licenses this file to you under the MIT license.
//!
//! This is generated file, see src/mono/wasm/runtime/rollup.config.js
//!
//! This is generated file, see src/mono/wasm/runtime/rollup.config.js
//! This is not considered public API with backward compatibility guarantees.
declare interface ManagedPointer {
__brandManagedPointer: "ManagedPointer";
@ -197,7 +199,7 @@ declare type DotnetModuleConfig = {
onConfigLoaded?: () => void;
onDotnetReady?: () => void;
imports?: DotnetModuleConfigImports;
} & EmscriptenModule;
} & Partial<EmscriptenModule>;
declare type DotnetModuleConfigImports = {
require?: (name: string) => any;
fetch?: (url: string) => Promise<Response>;
@ -237,7 +239,7 @@ declare function mono_wasm_load_config(configFilePath: string): Promise<void>;
declare function mono_wasm_load_icu_data(offset: VoidPtr): boolean;
declare function conv_string(mono_obj: MonoString): string | null;
declare function js_string_to_mono_string(string: string): MonoString | null;
declare function js_string_to_mono_string(string: string): MonoString;
declare function js_to_mono_obj(js_obj: any): MonoObject;
declare function js_typed_array_to_array(js_obj: any): MonoArray;

View File

@ -1,5 +1,5 @@
{
"name": "dotnet-runtime",
"name": "@microsoft/dotnet-runtime",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,

View File

@ -1,5 +1,5 @@
{
"name": "dotnet-runtime",
"name": "@microsoft/dotnet-runtime",
"description": ".NET is a developer platform with tools and libraries for building any type of app, including web, mobile, desktop, games, IoT, cloud, and microservices.",
"repository": {
"type": "git",

View File

@ -41,7 +41,7 @@ const terserConfig = {
};
const plugins = isDebug ? [writeOnChangePlugin()] : [terser(terserConfig), writeOnChangePlugin()];
const banner = "//! Licensed to the .NET Foundation under one or more agreements.\n//! The .NET Foundation licenses this file to you under the MIT license.\n";
const banner_generated = banner + "//! \n//! This is generated file, see src/mono/wasm/runtime/rollup.config.js \n";
const banner_dts = banner + "//!\n//! This is generated file, see src/mono/wasm/runtime/rollup.config.js\n\n//! This is not considered public API with backward compatibility guarantees. \n";
// emcc doesn't know how to load ES6 module, that's why we need the whole rollup.js
const format = "iife";
const name = "__dotnet_runtime";
@ -80,7 +80,7 @@ const typesConfig = {
{
format: "es",
file: nativeBinDir + "/dotnet.d.ts",
banner: banner_generated,
banner: banner_dts,
plugins: [writeOnChangePlugin()],
}
],
@ -93,7 +93,7 @@ if (isDebug) {
typesConfig.output.push({
format: "es",
file: "./dotnet.d.ts",
banner: banner_generated,
banner: banner_dts,
plugins: [alwaysLF(), writeOnChangePlugin()],
});
}

View File

@ -163,9 +163,9 @@ export function js_string_to_mono_string_interned(string: string | symbol): Mono
return ptr;
}
export function js_string_to_mono_string(string: string): MonoString | null {
export function js_string_to_mono_string(string: string): MonoString {
if (string === null)
return null;
return MonoStringNull;
else if (typeof (string) === "symbol")
return js_string_to_mono_string_interned(string);
else if (typeof (string) !== "string")

View File

@ -166,7 +166,7 @@ export type DotnetModuleConfig = {
onDotnetReady?: () => void;
imports?: DotnetModuleConfigImports;
} & EmscriptenModule
} & Partial<EmscriptenModule>
export type DotnetModuleConfigImports = {
require?: (name: string) => any;

View File

@ -192,8 +192,7 @@
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(ICULibFiles);
@(ICULibNativeFiles);
runtime/package.json;"
@(ICULibNativeFiles);"
DestinationFolder="$(NativeBinDir)"
SkipUnchangedFiles="true" />
@ -253,7 +252,6 @@
<Touch Files="$(MonoProjectRoot)wasm/runtime/node_modules/.npm-stamp" AlwaysCreate="true" />
</Target>
<ItemGroup>
<_RollupInputs Include="$(MonoProjectRoot)wasm/runtime/*.ts"/>
<_RollupInputs Include="$(MonoProjectRoot)wasm/runtime/types/*.ts"/>
@ -272,6 +270,14 @@
<!-- compile typescript -->
<RunWithEmSdkEnv Command="npm run rollup -- --environment Configuration:$(Configuration),NativeBinDir:$(NativeBinDir),ProductVersion:$(ProductVersion)" EmSdkPath="$(EMSDK_PATH)" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(MonoProjectRoot)wasm/runtime/"/>
<Copy SourceFiles="runtime/package.json;"
DestinationFolder="$(NativeBinDir)"
SkipUnchangedFiles="true" />
<!-- set version -->
<RunWithEmSdkEnv Command="npm version $(PackageVersion)" EmSdkPath="$(EMSDK_PATH)" WorkingDirectory="$(NativeBinDir)"/>
<Touch Files="$(NativeBinDir).rollup-stamp" AlwaysCreate="true" />
</Target>
</Project>