зеркало из https://github.com/dotnet/razor.git
Blazor WebAssembly Debug extension is not working with .net7 (#7929)
* We cannot use the BrowserDebugProxy from the extension folder, that one only works with .net6 app, and it should work with .net6 and .net7, so we should use the inspectURI which will start the correct browserDebugProxy version. * try to get the inspectUri from the launchSettings.json file, if it cannot be found then use the default one, this will make possible debug the new browserwasm template * avoiding exception if profiles doesn't exist
This commit is contained in:
Родитель
bdedec86b9
Коммит
b412228ed6
|
@ -154,5 +154,4 @@ yarn-*.log
|
||||||
*.svclog
|
*.svclog
|
||||||
|
|
||||||
# Bundled extension assets
|
# Bundled extension assets
|
||||||
src/Razor/src/Microsoft.AspNetCore.Razor.VSCode.BlazorWasmDebuggingExtension/BlazorDebugProxy/
|
|
||||||
*.vsix
|
*.vsix
|
||||||
|
|
|
@ -22,8 +22,6 @@ To that end, we need a way to launch the debugging proxy on the user's host mach
|
||||||
|
|
||||||
In order to publish the extension, you will need to have access to the `ms.dotnet-tools` publisher account on the VS Code marketplace. If you don't already have this access, reach out to @captainsafia for info.
|
In order to publish the extension, you will need to have access to the `ms.dotnet-tools` publisher account on the VS Code marketplace. If you don't already have this access, reach out to @captainsafia for info.
|
||||||
|
|
||||||
This extension bundles the debugging proxy assets that are needed inside the `BlazorDebugProxy` directory. These assets are not committed to repository so they will need to be included as part of the publish process.
|
|
||||||
|
|
||||||
1. Generate a personal access token per the instructions in [the VS Code publishing guide](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page).
|
1. Generate a personal access token per the instructions in [the VS Code publishing guide](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page).
|
||||||
2. Store the token from #1 in the `VSCODE_MARKETPLACE_TOKEN` environment variable.
|
2. Store the token from #1 in the `VSCODE_MARKETPLACE_TOKEN` environment variable.
|
||||||
3. Increment the `patch` version of the package in the `package.json` file.
|
3. Increment the `patch` version of the package in the `package.json` file.
|
||||||
|
|
|
@ -22,17 +22,13 @@
|
||||||
],
|
],
|
||||||
"publisher": "ms-dotnettools",
|
"publisher": "ms-dotnettools",
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"onCommand:blazorwasm-companion.launchDebugProxy",
|
"onCommand:blazorwasm-companion.launchDebugProxy"
|
||||||
"onCommand:blazorwasm-companion.killDebugProxy"
|
|
||||||
],
|
],
|
||||||
"main": "./dist/extension.js",
|
"main": "./dist/extension.js",
|
||||||
"extensionDependencies": [
|
"extensionDependencies": [
|
||||||
"ms-dotnettools.vscode-dotnet-runtime",
|
"ms-dotnettools.vscode-dotnet-runtime",
|
||||||
"ms-dotnettools.csharp"
|
"ms-dotnettools.csharp"
|
||||||
],
|
],
|
||||||
"files": [
|
|
||||||
"BlazorDebugProxy/"
|
|
||||||
],
|
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"untrustedWorkspaces": {
|
"untrustedWorkspaces": {
|
||||||
"supported": true
|
"supported": true
|
||||||
|
@ -42,7 +38,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"vscode:prepublish": "yarn run build",
|
"vscode:prepublish": "yarn run build",
|
||||||
"clean": "rimraf dist",
|
"clean": "rimraf dist",
|
||||||
"build": "yarn run clean && yarn run lint && yarn run compile && node scripts/retrieveDebugProxy.js",
|
"build": "yarn run clean && yarn run lint && yarn run compile",
|
||||||
"compile": "tsc -p ./",
|
"compile": "tsc -p ./",
|
||||||
"watch": "tsc -watch -p ./",
|
"watch": "tsc -watch -p ./",
|
||||||
"pretest": "yarn run compile && yarn run lint",
|
"pretest": "yarn run compile && yarn run lint",
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
/* --------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
* ------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
const fetch = require('node-fetch');
|
|
||||||
const stream = require('stream');
|
|
||||||
const extract = require('extract-zip');
|
|
||||||
const fs = require('fs');
|
|
||||||
const util = require('util');
|
|
||||||
const path = require('path');
|
|
||||||
const os = require('os');
|
|
||||||
|
|
||||||
const finished = util.promisify(stream.finished);
|
|
||||||
|
|
||||||
const formatLog = (text) => `[${new Date()}] ${text}`;
|
|
||||||
const log = (text) => console.log(formatLog(text));
|
|
||||||
const logError = (text) => console.error(formatLog(text));
|
|
||||||
|
|
||||||
async function downloadProxyPackage(version) {
|
|
||||||
const tmpDirectory = path.join(os.tmpdir(), 'blazorwasm-companion-tmp');
|
|
||||||
if (!fs.existsSync(tmpDirectory)) {
|
|
||||||
fs.mkdirSync(tmpDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// nuget.org requires the package name be lower-case
|
|
||||||
const nugetUrl = 'https://api.nuget.org/v3-flatcontainer';
|
|
||||||
const packageName = 'Microsoft.AspNetCore.Components.WebAssembly.DevServer'.toLowerCase();
|
|
||||||
const versionedPackageName = `${packageName}.${version}.nupkg`;
|
|
||||||
const downloadUrl = `${nugetUrl}/${packageName}/${version}/${versionedPackageName}`;
|
|
||||||
|
|
||||||
// Download and save nupkg to disk
|
|
||||||
log(`Fetching package from ${downloadUrl}...`);
|
|
||||||
const response = await fetch(downloadUrl);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
logError(`Failed to download ${downloadUrl}`);
|
|
||||||
throw new Error(`Unable to download BlazorDebugProxy: ${response.status} ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const downloadPath = path.join(tmpDirectory, versionedPackageName);
|
|
||||||
const outputStream = fs.createWriteStream(downloadPath);
|
|
||||||
response.body.pipe(outputStream);
|
|
||||||
await finished(outputStream);
|
|
||||||
|
|
||||||
// Extract nupkg to extraction directory
|
|
||||||
log(`Extracting NuGet package with directory...`)
|
|
||||||
const extractTarget = path.join(tmpDirectory, `extracted-${packageName}.${version}`);
|
|
||||||
await extract(downloadPath, { dir: extractTarget });
|
|
||||||
return extractTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function copyDebugProxyAssets(version) {
|
|
||||||
const targetDirectory = path.join(__dirname, '..', 'BlazorDebugProxy', version);
|
|
||||||
if (fs.existsSync(targetDirectory)) {
|
|
||||||
log(`BlazorDebugProxy ${version} is already downloaded, nothing to do.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log(`Downloading BlazorDebugProxy ${version}...`);
|
|
||||||
const extracted = await downloadProxyPackage(version);
|
|
||||||
|
|
||||||
log(`Using ${targetDirectory} as targetDirectory...`);
|
|
||||||
fs.mkdirSync(targetDirectory, { recursive: true });
|
|
||||||
|
|
||||||
const srcDirectory = path.join(extracted, 'tools', 'BlazorDebugProxy');
|
|
||||||
log(`Copying BlazorDebugProxy assets from ${srcDirectory} to ${targetDirectory}...`);
|
|
||||||
fs.readdirSync(srcDirectory).forEach(function(file) {
|
|
||||||
log(`Copying ${file} to target directory...`);
|
|
||||||
fs.copyFileSync(path.join(srcDirectory, file), path.join(targetDirectory, file));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const debugProxyVersion = require('../package.json').debugProxyVersion;
|
|
||||||
copyDebugProxyAssets(debugProxyVersion);
|
|
|
@ -1,34 +0,0 @@
|
||||||
/* --------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
* ------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
import { commands, extensions, OutputChannel } from 'vscode';
|
|
||||||
|
|
||||||
interface IDotnetAcquireResult {
|
|
||||||
dotnetPath: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function acquireDotnetInstall(outputChannel: OutputChannel): Promise<string> {
|
|
||||||
const extension = extensions.getExtension('ms-dotnettools.blazorwasm-companion');
|
|
||||||
const requestingExtensionId = 'blazorwasm-companion';
|
|
||||||
|
|
||||||
const version: string = extension && extension.packageJSON ? extension.packageJSON.dotnetRuntimeVersion : '6.0';
|
|
||||||
if (version.split('.').length !== 2) {
|
|
||||||
throw new Error('Version should be a valid major.minor version (the latest patch will automatically be selected).');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const dotnetResult = await commands.executeCommand<IDotnetAcquireResult>('dotnet.acquire', { version, requestingExtensionId });
|
|
||||||
const dotnetPath = dotnetResult?.dotnetPath;
|
|
||||||
if (!dotnetPath) {
|
|
||||||
throw new Error('Install step returned an undefined path.');
|
|
||||||
}
|
|
||||||
await commands.executeCommand('dotnet.ensureDotnetDependencies', { command: dotnetPath, arguments: ['--info'] });
|
|
||||||
return dotnetPath;
|
|
||||||
} catch (error) {
|
|
||||||
outputChannel.appendLine(`This extension requires .NET Core to run but we were unable to install it due to the following error:`);
|
|
||||||
outputChannel.appendLine((error as Error).message);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,89 +3,23 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
* ------------------------------------------------------------------------------------------ */
|
* ------------------------------------------------------------------------------------------ */
|
||||||
|
|
||||||
import * as cp from 'child_process';
|
import { readFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { acquireDotnetInstall } from './acquireDotnetInstall';
|
|
||||||
import { getAvailablePort } from './getAvailablePort';
|
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext) {
|
export async function activate(context: vscode.ExtensionContext) {
|
||||||
const outputChannel = vscode.window.createOutputChannel('Blazor WASM Debug Proxy');
|
const launchDebugProxy = vscode.commands.registerCommand('blazorwasm-companion.launchDebugProxy', async (folder: vscode.WorkspaceFolder) => {
|
||||||
const pidsByUrl = new Map<string, number>();
|
const launchSettings = JSON.parse(readFileSync(join(fileURLToPath(folder.uri.toString()), 'Properties', 'launchSettings.json'), 'utf8'));
|
||||||
|
if (launchSettings?.profiles && launchSettings?.profiles[Object.keys(launchSettings.profiles)[0]]?.inspectUri) {
|
||||||
const launchDebugProxy = vscode.commands.registerCommand('blazorwasm-companion.launchDebugProxy', async () => {
|
return {
|
||||||
try {
|
inspectUri: launchSettings.profiles[Object.keys(launchSettings.profiles)[0]].inspectUri,
|
||||||
const debuggingPort = await getAvailablePort(9222);
|
};
|
||||||
const debuggingHost = `http://localhost:${debuggingPort}`;
|
|
||||||
|
|
||||||
const extension = vscode.extensions.getExtension('ms-dotnettools.blazorwasm-companion');
|
|
||||||
const version: string = extension?.packageJSON.debugProxyVersion;
|
|
||||||
|
|
||||||
const debugProxyLocalPath = `${context.extensionPath}/BlazorDebugProxy/${version}/BrowserDebugHost.dll`;
|
|
||||||
const spawnedProxyArgs = [debugProxyLocalPath , '--DevToolsUrl', debuggingHost];
|
|
||||||
|
|
||||||
const dotnet = await acquireDotnetInstall(outputChannel);
|
|
||||||
|
|
||||||
outputChannel.appendLine(`Launching debugging proxy from ${debugProxyLocalPath}`);
|
|
||||||
const spawnedProxy = cp.spawn(dotnet, spawnedProxyArgs);
|
|
||||||
|
|
||||||
try {
|
|
||||||
let chunksProcessed = 0;
|
|
||||||
for await (const output of spawnedProxy.stdout) {
|
|
||||||
// If we haven't found the URL in the first ten chunks processed
|
|
||||||
// then bail out.
|
|
||||||
if (chunksProcessed++ > 10) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
outputChannel.appendLine(output);
|
|
||||||
// The debug proxy server outputs the port it is listening on in the
|
|
||||||
// standard output of the launched application. We need to pass this URL
|
|
||||||
// back to the debugger so we extract the URL from stdout using a regex.
|
|
||||||
// The debug proxy will not exit until killed via the `killDebugProxy`
|
|
||||||
// method so parsing stdout is necessary to extract the URL.
|
|
||||||
const matchExpr = 'Now listening on: (?<url>.*)';
|
|
||||||
const found = `${output}`.match(matchExpr);
|
|
||||||
const url = found?.groups?.url;
|
|
||||||
if (url) {
|
|
||||||
outputChannel.appendLine(`Debugging proxy is running at: ${url}`);
|
|
||||||
pidsByUrl.set(url, spawnedProxy.pid);
|
|
||||||
return {
|
|
||||||
url,
|
|
||||||
inspectUri: `${url}{browserInspectUriPath}`,
|
|
||||||
debuggingPort,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for await (const error of spawnedProxy.stderr) {
|
|
||||||
outputChannel.appendLine(`ERROR: ${error}`);
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
if (spawnedProxy.pid) {
|
|
||||||
outputChannel.appendLine(`Error occured while spawning debug proxy. Terminating debug proxy server.`);
|
|
||||||
process.kill(spawnedProxy.pid);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
outputChannel.appendLine(`ERROR: ${error}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
inspectUri: '{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}',
|
inspectUri: '{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const killDebugProxy = vscode.commands.registerCommand('blazorwasm-companion.killDebugProxy', (url: string) => {
|
context.subscriptions.push(launchDebugProxy);
|
||||||
const pid = pidsByUrl.get(url);
|
|
||||||
|
|
||||||
if (!pid) {
|
|
||||||
outputChannel.appendLine(`Unable to find PID for server running at ${url}.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
outputChannel.appendLine(`Terminating debug proxy server running at ${url} with PID ${pid}.`);
|
|
||||||
process.kill(pid);
|
|
||||||
});
|
|
||||||
|
|
||||||
context.subscriptions.push(launchDebugProxy, killDebugProxy);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
/* --------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
* ------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
import * as net from 'net';
|
|
||||||
|
|
||||||
export function getAvailablePort(initialPort: number) {
|
|
||||||
function getNextAvailablePort(currentPort: number, cb: (port: number) => void) {
|
|
||||||
const server = net.createServer();
|
|
||||||
server.listen(currentPort, () => {
|
|
||||||
server.once('close', () => {
|
|
||||||
cb(currentPort);
|
|
||||||
});
|
|
||||||
server.close();
|
|
||||||
});
|
|
||||||
server.on('error', () => {
|
|
||||||
if (currentPort <= 65535 /* total number of ports available */) {
|
|
||||||
getNextAvailablePort(++currentPort, cb);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise<number>(resolve => {
|
|
||||||
getNextAvailablePort(initialPort, resolve);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ import * as vscode from 'vscode';
|
||||||
|
|
||||||
import { RazorLogger } from '../RazorLogger';
|
import { RazorLogger } from '../RazorLogger';
|
||||||
import { JS_DEBUG_NAME, SERVER_APP_NAME } from './Constants';
|
import { JS_DEBUG_NAME, SERVER_APP_NAME } from './Constants';
|
||||||
import { isValidEvent, onDidTerminateDebugSession } from './TerminateDebugHandler';
|
import { onDidTerminateDebugSession } from './TerminateDebugHandler';
|
||||||
|
|
||||||
export class BlazorDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
|
export class BlazorDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export class BlazorDebugConfigurationProvider implements vscode.DebugConfigurati
|
||||||
url: string,
|
url: string,
|
||||||
inspectUri: string,
|
inspectUri: string,
|
||||||
debuggingPort: number,
|
debuggingPort: number,
|
||||||
}>('blazorwasm-companion.launchDebugProxy');
|
}>('blazorwasm-companion.launchDebugProxy', folder);
|
||||||
|
|
||||||
await this.launchBrowser(
|
await this.launchBrowser(
|
||||||
folder,
|
folder,
|
||||||
|
@ -35,15 +35,6 @@ export class BlazorDebugConfigurationProvider implements vscode.DebugConfigurati
|
||||||
result ? result.inspectUri : undefined,
|
result ? result.inspectUri : undefined,
|
||||||
result ? result.debuggingPort : undefined);
|
result ? result.debuggingPort : undefined);
|
||||||
|
|
||||||
if (result && result.url) {
|
|
||||||
const terminateDebugProxy = this.vscodeType.debug.onDidTerminateDebugSession(async event => {
|
|
||||||
if (isValidEvent(event.name)) {
|
|
||||||
await vscode.commands.executeCommand('blazorwasm-companion.killDebugProxy', result.url);
|
|
||||||
terminateDebugProxy.dispose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If `resolveDebugConfiguration` returns undefined, then the debugger
|
* If `resolveDebugConfiguration` returns undefined, then the debugger
|
||||||
* launch is canceled. Here, we opt to manually launch the browser
|
* launch is canceled. Here, we opt to manually launch the browser
|
||||||
|
|
Загрузка…
Ссылка в новой задаче