diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets
index 5ea1ca5525a2a..855ad32118d3d 100644
--- a/eng/liveBuilds.targets
+++ b/eng/liveBuilds.targets
@@ -195,8 +195,10 @@
+
+
diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
index 5cf78e89c3c15..4186a7a43b17b 100644
--- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
+++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
@@ -208,6 +208,11 @@ Copyright (c) .NET Foundation. All rights reserved.
Condition="@(WasmNativeAsset->Count()) > 0 and ( '%(FileName)' == 'dotnet' or '%(FileName)' == 'dotnet.native' ) and ('%(Extension)' == '.wasm' or '%(Extension)' == '.js')" />
+
+ <_WasmEmitSourceMapBuild>$(WasmEmitSourceMap)
+ <_WasmEmitSourceMapBuild Condition="'$(_WasmEmitSourceMapBuild)' == ''">true
+
+
@@ -376,6 +382,11 @@ Copyright (c) .NET Foundation. All rights reserved.
Condition="'%(StaticWebAsset.AssetTraitName)' == 'WasmResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture' or '%(AssetRole)' == 'Alternative'" />
+
+ <_WasmEmitSourceMapPublish>$(WasmEmitSourceMap)
+ <_WasmEmitSourceMapPublish Condition="'$(_WasmEmitSourceMapPublish)' == ''">false
+
+
diff --git a/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs b/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
index 8e0f23548a575..433af91ec0f16 100644
--- a/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
@@ -685,8 +685,10 @@ protected static void AssertBasicAppBundle(string bundleDir,
"_framework/dotnet.native.wasm",
"_framework/blazor.boot.json",
"_framework/dotnet.js",
+ "_framework/dotnet.js.map",
"_framework/dotnet.native.js",
- "_framework/dotnet.runtime.js"
+ "_framework/dotnet.runtime.js",
+ "_framework/dotnet.runtime.js.map",
};
if (isBrowserProject)
diff --git a/src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
index d190dc2199410..7bdc4c2c45ef6 100644
--- a/src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
@@ -186,7 +186,9 @@ internal void CompareStat(IDictionary oldStat, IDictionary
+
+
diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts
index 50c6324eeb0f0..b715912b04ef3 100644
--- a/src/mono/wasm/runtime/dotnet.d.ts
+++ b/src/mono/wasm/runtime/dotnet.d.ts
@@ -309,6 +309,7 @@ interface BootJsonData {
readonly resources: ResourceGroups;
/** Gets a value that determines if this boot config was produced from a non-published build (i.e. dotnet build or dotnet run) */
readonly debugBuild: boolean;
+ readonly debugLevel: number;
readonly linkerEnabled: boolean;
readonly cacheBootResources: boolean;
readonly config: string[];
diff --git a/src/mono/wasm/runtime/package.json b/src/mono/wasm/runtime/package.json
index 249235b29c5ff..4f247bff7cea4 100644
--- a/src/mono/wasm/runtime/package.json
+++ b/src/mono/wasm/runtime/package.json
@@ -22,11 +22,12 @@
"author": "Microsoft",
"license": "MIT",
"devDependencies": {
+ "@rollup/plugin-terser": "0.4.1",
"@rollup/plugin-typescript": "11.1.0",
"@rollup/plugin-virtual": "3.0.1",
- "@rollup/plugin-terser": "0.4.1",
"@typescript-eslint/eslint-plugin": "5.59.1",
"@typescript-eslint/parser": "5.59.1",
+ "magic-string": "0.30.0",
"eslint": "8.39.0",
"fast-glob": "3.2.12",
"git-commit-info": "2.0.1",
diff --git a/src/mono/wasm/runtime/rollup.config.js b/src/mono/wasm/runtime/rollup.config.js
index 9d1074488e1cd..dd4b704d857b9 100644
--- a/src/mono/wasm/runtime/rollup.config.js
+++ b/src/mono/wasm/runtime/rollup.config.js
@@ -10,9 +10,11 @@ import dts from "rollup-plugin-dts";
import { createFilter } from "@rollup/pluginutils";
import fast_glob from "fast-glob";
import gitCommitInfo from "git-commit-info";
+import MagicString from "magic-string";
const configuration = process.env.Configuration;
const isDebug = configuration !== "Release";
+const isContinuousIntegrationBuild = process.env.ContinuousIntegrationBuild === "true" ? true : false;
const productVersion = process.env.ProductVersion || "8.0.0-dev";
const nativeBinDir = process.env.NativeBinDir ? process.env.NativeBinDir.replace(/"/g, "") : "bin";
const monoWasmThreads = process.env.MonoWasmThreads === "true" ? true : false;
@@ -38,13 +40,15 @@ const banner_dts = banner + "//!\n//! This is generated file, see src/mono/wasm/
// emcc doesn't know how to load ES6 module, that's why we need the whole rollup.js
const inlineAssert = [
{
- pattern: /mono_assert\(([^,]*), *"([^"]*)"\);/gm,
// eslint-disable-next-line quotes
- replacement: 'if (!($1)) throw new Error("Assert failed: $2"); // inlined mono_assert'
+ pattern: 'mono_assert\\(([^,]*), *"([^"]*)"\\);',
+ // eslint-disable-next-line quotes
+ replacement: (match) => `if (!(${match[1]})) throw new Error("Assert failed: ${match[2]}"); // inlined mono_assert`
},
{
- pattern: /mono_assert\(([^,]*), \(\) => *`([^`]*)`\);/gm,
- replacement: "if (!($1)) throw new Error(`Assert failed: $2`); // inlined mono_assert"
+ // eslint-disable-next-line quotes
+ pattern: 'mono_assert\\(([^,]*), \\(\\) => *`([^`]*)`\\);',
+ replacement: (match) => `if (!(${match[1]})) throw new Error(\`Assert failed: ${match[2]}\`); // inlined mono_assert`
}
];
const checkAssert =
@@ -78,8 +82,28 @@ const envConstants = {
monoDiagnosticsMock,
gitHash,
wasmEnableLegacyJsInterop,
+ isContinuousIntegrationBuild,
};
+const locationCache = {};
+function sourcemapPathTransform(relativeSourcePath, sourcemapPath) {
+ let res = locationCache[relativeSourcePath];
+ if (res === undefined) {
+ if (!isContinuousIntegrationBuild) {
+ const sourcePath = path.resolve(
+ path.dirname(sourcemapPath),
+ relativeSourcePath
+ );
+ res = `file:///${sourcePath.replace(/\\/g, "/")}`;
+ } else {
+ relativeSourcePath = relativeSourcePath.substring(12);
+ res = `https://raw.githubusercontent.com/dotnet/runtime/${gitHash}/${relativeSourcePath}`;
+ }
+ locationCache[relativeSourcePath] = res;
+ }
+ return res;
+}
+
function consts(dict) {
// implement rollup-plugin-const in terms of @rollup/plugin-virtual
// It's basically the same thing except "consts" names all its modules with a "consts:" prefix,
@@ -103,7 +127,7 @@ const typescriptConfigOptions = {
};
const outputCodePlugins = [consts(envConstants), typescript(typescriptConfigOptions)];
-const externalDependencies = ["module"];
+const externalDependencies = ["module", "process"];
const loaderConfig = {
treeshake: !isDebug,
@@ -114,25 +138,14 @@ const loaderConfig = {
file: nativeBinDir + "/dotnet.js",
banner,
plugins,
+ sourcemap: true,
+ sourcemapPathTransform,
}
],
external: externalDependencies,
plugins: [regexReplace(inlineAssert), regexCheck([checkAssert, checkNoRuntime]), ...outputCodePlugins],
onwarn: onwarn
};
-const typesConfig = {
- input: "./types/export-types.ts",
- output: [
- {
- format: "es",
- file: nativeBinDir + "/dotnet.d.ts",
- banner: banner_dts,
- plugins: [writeOnChangePlugin()],
- }
- ],
- external: externalDependencies,
- plugins: [dts()],
-};
const runtimeConfig = {
treeshake: !isDebug,
input: "exports.ts",
@@ -142,13 +155,28 @@ const runtimeConfig = {
file: nativeBinDir + "/dotnet.runtime.js",
banner,
plugins,
+ sourcemap: true,
+ sourcemapPathTransform,
}
],
external: externalDependencies,
plugins: [regexReplace(inlineAssert), regexCheck([checkAssert, checkNoLoader]), ...outputCodePlugins],
onwarn: onwarn
};
-const legacyConfig = {
+const typesConfig = {
+ input: "./types/export-types.ts",
+ output: [
+ {
+ format: "es",
+ file: nativeBinDir + "/dotnet.d.ts",
+ banner: banner_dts,
+ plugins: [writeOnChangePlugin()],
+ }
+ ],
+ external: externalDependencies,
+ plugins: [dts()],
+};
+const legacyTypesConfig = {
input: "./net6-legacy/export-types.ts",
output: [
{
@@ -174,7 +202,7 @@ if (isDebug) {
banner: banner_dts,
plugins: [alwaysLF(), writeOnChangePlugin()],
});
- legacyConfig.output.push({
+ legacyTypesConfig.output.push({
format: "es",
file: "./dotnet-legacy.d.ts",
banner: banner_dts,
@@ -221,7 +249,7 @@ const allConfigs = [
loaderConfig,
runtimeConfig,
typesConfig,
- legacyConfig,
+ legacyTypesConfig,
].concat(workerConfigs)
.concat(diagnosticMockTypesConfig ? [diagnosticMockTypesConfig] : []);
export default defineConfig(allConfigs);
@@ -336,19 +364,34 @@ function regexReplace(replacements = []) {
}
};
- function executeReplacement(_, code) {
- // TODO use MagicString for sourcemap support
- let fixed = code;
- for (const rep of replacements) {
- const { pattern, replacement } = rep;
- fixed = fixed.replace(pattern, replacement);
+ function executeReplacement(_, code, id) {
+ const magicString = new MagicString(code);
+ if (!codeHasReplacements(code, id, magicString)) {
+ return null;
}
- if (fixed == code) {
- return null;
+ const result = { code: magicString.toString() };
+ result.map = magicString.generateMap({ hires: true });
+ return result;
+ }
+
+ function codeHasReplacements(code, id, magicString) {
+ let result = false;
+ let match;
+ for (const rep of replacements) {
+ const { pattern, replacement } = rep;
+ const rx = new RegExp(pattern, "gm");
+ while ((match = rx.exec(code))) {
+ result = true;
+ const updated = replacement(match);
+ const start = match.index;
+ const end = start + match[0].length;
+ magicString.overwrite(start, end, updated);
+ }
}
- return { code: fixed };
+ // eslint-disable-next-line no-cond-assign
+ return result;
}
}
diff --git a/src/mono/wasm/runtime/tsconfig.json b/src/mono/wasm/runtime/tsconfig.json
index 4353fe7e54bcb..c5a5693168aa2 100644
--- a/src/mono/wasm/runtime/tsconfig.json
+++ b/src/mono/wasm/runtime/tsconfig.json
@@ -5,5 +5,6 @@
"esnext",
"dom"
],
+ "sourceMap": true,
}
-}
+}
\ No newline at end of file
diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj
index c150a1fe09d31..2842fb19f2507 100644
--- a/src/mono/wasm/wasm.proj
+++ b/src/mono/wasm/wasm.proj
@@ -451,7 +451,9 @@
- Configuration:$(Configuration),NativeBinDir:$(NativeBinDir),ProductVersion:$(ProductVersion),MonoWasmThreads:$(MonoWasmThreads),DISABLE_LEGACY_JS_INTEROP:$(_DisableLegacyJsInterop),MonoDiagnosticsMock:$(MonoDiagnosticsMock)
+ Configuration:$(Configuration),NativeBinDir:$(NativeBinDir),ProductVersion:$(ProductVersion),MonoWasmThreads:$(MonoWasmThreads),DISABLE_LEGACY_JS_INTEROP:$(_DisableLegacyJsInterop),MonoDiagnosticsMock:$(MonoDiagnosticsMock),ContinuousIntegrationBuild:$(ContinuousIntegrationBuild)
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs
index e102f669d477f..445f427e02faf 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs
@@ -32,6 +32,7 @@ public static bool ShouldFilterCandidate(
bool copySymbols,
string customIcuCandidateFilename,
bool enableThreads,
+ bool emitSourceMap,
out string reason)
{
var extension = candidate.GetMetadata("Extension");
@@ -55,6 +56,7 @@ public static bool ShouldFilterCandidate(
".dat" when !string.IsNullOrEmpty(customIcuCandidateFilename) && fileName != customIcuCandidateFilename => "custom icu file will be used instead of icu from the runtime pack",
".json" when fromMonoPackage && (fileName == "emcc-props" || fileName == "package") => $"{fileName}{extension} is not used by Blazor",
".ts" when fromMonoPackage && fileName == "dotnet.d" => "dotnet type definition is not used by Blazor",
+ ".map" when !emitSourceMap && fromMonoPackage && (fileName == "dotnet.js" || fileName == "dotnet.runtime.js") => "source map file is not published",
".ts" when fromMonoPackage && fileName == "dotnet-legacy.d" => "dotnet type definition is not used by Blazor",
".js" when assetType == "native" && !(dotnetJsSingleThreadNames.Contains(fileName) || (enableThreads && fileName == "dotnet.native.worker")) => $"{fileName}{extension} is not used by Blazor",
".pdb" when !copySymbols => "copying symbols is disabled",
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs
index 580f5288b2ccb..ba25c5103c4d5 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs
@@ -50,6 +50,8 @@ public class ComputeWasmBuildAssets : Task
public bool EnableThreads { get; set; }
+ public bool EmitSourceMap { get; set; }
+
[Output]
public ITaskItem[] AssetCandidates { get; set; }
@@ -84,7 +86,7 @@ public override bool Execute()
for (int i = 0; i < Candidates.Length; i++)
{
var candidate = Candidates[i];
- if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, EnableThreads, out var reason))
+ if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, EnableThreads, EmitSourceMap, out var reason))
{
Log.LogMessage(MessageImportance.Low, "Skipping asset '{0}' because '{1}'", candidate.ItemSpec, reason);
filesToRemove.Add(candidate);
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs
index dd9075435c3c9..8a9bcc500dadc 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs
@@ -58,6 +58,8 @@ public class ComputeWasmPublishAssets : Task
public bool EnableThreads { get; set; }
+ public bool EmitSourceMap { get; set; }
+
public bool IsWebCilEnabled { get; set; }
[Output]
@@ -575,7 +577,7 @@ private void GroupResolvedFilesToPublish(
foreach (var candidate in resolvedFilesToPublish)
{
- if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, EnableThreads, out var reason))
+ if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, EnableThreads, EmitSourceMap, out var reason))
{
Log.LogMessage(MessageImportance.Low, "Skipping asset '{0}' because '{1}'", candidate.ItemSpec, reason);
if (!resolvedFilesToPublishToRemove.ContainsKey(candidate.ItemSpec))
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
index 88cf5faf255f3..12e7cb3a581c7 100644
--- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
+++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
@@ -150,6 +150,12 @@ protected override bool ExecuteInternal()
if (!IncludeThreadsWorker && name == "dotnet.native.worker.js")
continue;
+ if (name == "dotnet.runtime.js.map" || name == "dotnet.js.map")
+ {
+ Log.LogMessage(MessageImportance.Low, $"Skipping {item.ItemSpec} from boot config");
+ continue;
+ }
+
var itemHash = Utils.ComputeIntegrity(item.ItemSpec);
if (name.StartsWith("dotnet", StringComparison.OrdinalIgnoreCase) && string.Equals(Path.GetExtension(name), ".wasm", StringComparison.OrdinalIgnoreCase))