diff --git a/extensions/debug-server-ready/.vscodeignore b/extensions/debug-server-ready/.vscodeignore new file mode 100644 index 0000000000000..36e8b0714faf4 --- /dev/null +++ b/extensions/debug-server-ready/.vscodeignore @@ -0,0 +1,5 @@ +src/** +tsconfig.json +out/** +extension.webpack.config.js +yarn.lock \ No newline at end of file diff --git a/extensions/debug-server-ready/extension.webpack.config.js b/extensions/debug-server-ready/extension.webpack.config.js new file mode 100644 index 0000000000000..b474e65cbb130 --- /dev/null +++ b/extensions/debug-server-ready/extension.webpack.config.js @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + entry: { + extension: './src/extension.ts', + }, + resolve: { + mainFields: ['module', 'main'] + } +}); diff --git a/extensions/debug-server-ready/package.json b/extensions/debug-server-ready/package.json new file mode 100644 index 0000000000000..84a608f601a0c --- /dev/null +++ b/extensions/debug-server-ready/package.json @@ -0,0 +1,74 @@ +{ + "name": "debug-auto-launch", + "displayName": "%displayName%", + "description": "%description%", + "version": "1.0.0", + "publisher": "vscode", + "engines": { + "vscode": "^1.32.0" + }, + "activationEvents": [ + "onDebugResolve" + ], + "main": "./out/extension", + "scripts": { + "compile": "gulp compile-extension:debug-server-ready", + "watch": "gulp watch-extension:debug-server-ready" + }, + "contributes": { + "debuggers": [ + { + "type": "node", + "configurationAttributes": { + "launch": { + "properties": { + "serverReadyAction": { + "type": "object", + "markdownDescription": "Server Ready options.", + "default": { + "action": "openExternally" + }, + "properties": { + "pattern": { + "type": "string", + "markdownDescription": "Server is ready if this pattern appears on the debug console. The first group must include the port number.", + "default": "listening on port ([0-9]+)" + }, + "uriFormat": { + "type": "string", + "markdownDescription": "A format string used when constructing the URI. The first '%s' is substituted with the port number.", + "default": "http://localhost:%s" + }, + "action": { + "type": "string", + "enum": [ + "openExternally", + "debugWithChrome" + ], + "enumDescriptions": [ + "Open URI externally.", + "Start debugging with the Debugger for Chrome." + ], + "markdownDescription": "Determines what to do with when the server is ready.", + "default": "openExternally" + }, + "webRoot": { + "type": "string", + "markdownDescription": "Literally passed to chrome debug configuration.", + "default": "${workspaceFolder}" + } + } + } + } + } + } + } + ] + }, + "dependencies": { + "vscode-nls": "^4.0.0" + }, + "devDependencies": { + "@types/node": "8.0.33" + } +} diff --git a/extensions/debug-server-ready/package.nls.json b/extensions/debug-server-ready/package.nls.json new file mode 100644 index 0000000000000..60c41e8667744 --- /dev/null +++ b/extensions/debug-server-ready/package.nls.json @@ -0,0 +1,4 @@ +{ + "displayName": "Server Ready Action", + "description": "Open URI in browser if server under debugging is ready." +} \ No newline at end of file diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts new file mode 100644 index 0000000000000..b91d023da74af --- /dev/null +++ b/extensions/debug-server-ready/src/extension.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +//import * as nls from 'vscode-nls'; +import * as util from 'util'; + +const trackers = new Set(); + +const PATTERN = 'listening on.* (https?://\\S+|[0-9]+)'; // matches "listening on port 3000" or "Now listening on: https://localhost:5001" +const URI_FORMAT = 'http://localhost:%s'; +const WEB_ROOT = '${workspaceFolder}'; + +interface ServerReadyAction { + pattern: string; + action?: 'openExternally' | 'debugWithChrome'; + urlFormat?: string; + webRoot?: string; +} + +export function activate(context: vscode.ExtensionContext) { + + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('*', { + resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration) { + const args: ServerReadyAction = debugConfiguration.serverReadyAction; + if (debugConfiguration.type && args) { + startTrackerForType(context, debugConfiguration.type); + } + return debugConfiguration; + } + })); +} + +function startTrackerForType(context: vscode.ExtensionContext, type: string) { + + if (!trackers.has(type)) { + trackers.add(type); + + // scan debug console output for a PORT message + context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(type, { + createDebugAdapterTracker(session: vscode.DebugSession) { + const args: ServerReadyAction = session.configuration.serverReadyAction; + if (args) { + const regexp = new RegExp(args.pattern || PATTERN); + let hasFired = false; + return { + onDidSendMessage: m => { + if (!hasFired && m.type === 'event' && m.event === 'output' && m.body.output) { + const result = regexp.exec(m.body.output); + if (result && result.length === 2) { + openExternalWithString(session, result[1]); + hasFired = true; + } + } + } + }; + } + return undefined; + } + })); + } +} + +function openExternalWithString(session: vscode.DebugSession, portOrUriString: string) { + + if (portOrUriString) { + if (/^[0-9]+$/.test(portOrUriString)) { + const args: ServerReadyAction = session.configuration.serverReadyAction; + portOrUriString = util.format(args.urlFormat || URI_FORMAT, portOrUriString); + } + openExternalWithUri(session, portOrUriString); + } +} + +function openExternalWithUri(session: vscode.DebugSession, uri: string) { + + const args: ServerReadyAction = session.configuration.serverReadyAction; + switch (args.action || 'openExternally') { + case 'openExternally': + vscode.env.openExternal(vscode.Uri.parse(uri)); + break; + case 'debugWithChrome': + vscode.debug.startDebugging(session.workspaceFolder, { + type: 'chrome', + name: 'Chrome Debug', + request: 'launch', + url: uri, + webRoot: args.webRoot || WEB_ROOT + }); + break; + default: + // not supported + break; + } +} diff --git a/extensions/debug-server-ready/src/typings/ref.d.ts b/extensions/debug-server-ready/src/typings/ref.d.ts new file mode 100644 index 0000000000000..bc057c5587839 --- /dev/null +++ b/extensions/debug-server-ready/src/typings/ref.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/debug-server-ready/tsconfig.json b/extensions/debug-server-ready/tsconfig.json new file mode 100644 index 0000000000000..f9b780a0e1cf3 --- /dev/null +++ b/extensions/debug-server-ready/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out", + "downlevelIteration": true + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/debug-server-ready/yarn.lock b/extensions/debug-server-ready/yarn.lock new file mode 100644 index 0000000000000..6767cb8d8c22e --- /dev/null +++ b/extensions/debug-server-ready/yarn.lock @@ -0,0 +1,13 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@8.0.33": + version "8.0.33" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" + integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== + +vscode-nls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" + integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==