diff --git a/.github/ISSUE_TEMPLATE/do-not-file-issues-here-.md b/.github/ISSUE_TEMPLATE/do-not-file-issues-here-.md deleted file mode 100644 index 480151ad..00000000 --- a/.github/ISSUE_TEMPLATE/do-not-file-issues-here-.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: Do not file issues here! -about: 'VS Code has a new debugger which can be found in the vscode-js-debug repo ' -title: '' -labels: '' -assignees: '' - ---- - ---- -name: Do not file issues here! -about: VS Code has a new debugger which can be found in the vscode-js-debug repo -title: '' -labels: '' -assignees: '' - ---- - - diff --git a/.github/assignment.yml b/.github/assignment.yml deleted file mode 100644 index 8ba5d285..00000000 --- a/.github/assignment.yml +++ /dev/null @@ -1,4 +0,0 @@ -{ - perform: true, - assignees: [ roblourens ] -} \ No newline at end of file diff --git a/.github/locker.yml b/.github/locker.yml deleted file mode 100644 index 2186b074..00000000 --- a/.github/locker.yml +++ /dev/null @@ -1,5 +0,0 @@ -{ - daysAfterClose: 30, - daysSinceLastUpdate: 3, - perform: true -} \ No newline at end of file diff --git a/.github/needs_more_info.yml b/.github/needs_more_info.yml deleted file mode 100644 index 934e9889..00000000 --- a/.github/needs_more_info.yml +++ /dev/null @@ -1,6 +0,0 @@ -{ - daysUntilClose: 7, - needsMoreInfoLabel: 'needs more info', - perform: true, - closeComment: 'This issue has been closed automatically because it needs more information and has not had recent activity. Please refer to our [guidelines](https://github.com/Microsoft/vscode/blob/master/CONTRIBUTING.md) for filing issues. Thank you for your contributions.' -} diff --git a/Source/errors.ts b/Source/errors.ts new file mode 100644 index 00000000..9d98e10b --- /dev/null +++ b/Source/errors.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { DebugProtocol } from "vscode-debugprotocol"; + +import * as nls from "vscode-nls"; +import { ErrorWithMessage } from "vscode-chrome-debug-core"; +const localize = nls.loadMessageBundle(); + +export function runtimeNotFound(_runtime: string): DebugProtocol.Message { + return new ErrorWithMessage({ + id: 2001, + format: localize( + "VSND2001", + "Cannot find runtime '{0}' on PATH. Is '{0}' installed?", + "{_runtime}" + ), + variables: { _runtime }, + }); +} + +export function cannotLaunchInTerminal(_error: string): DebugProtocol.Message { + return new ErrorWithMessage({ + id: 2011, + format: localize( + "VSND2011", + "Cannot launch debug target in terminal ({0}).", + "{_error}" + ), + variables: { _error }, + }); +} + +export function cannotLaunchDebugTarget(_error: string): DebugProtocol.Message { + return new ErrorWithMessage({ + id: 2017, + format: localize( + "VSND2017", + "Cannot launch debug target ({0}).", + "{_error}" + ), + variables: { _error }, + showUser: true, + sendTelemetry: true, + }); +} + +export function cannotDebugExtension(_error: string): DebugProtocol.Message { + return new ErrorWithMessage({ + id: 2035, + format: localize( + "VSND2035", + "Cannot debug extension ({0}).", + "{_error}" + ), + variables: { _error }, + showUser: true, + sendTelemetry: true, + }); +} + +export function unknownConsoleType(consoleType: string): DebugProtocol.Message { + return new ErrorWithMessage({ + id: 2028, + format: localize( + "VSND2028", + "Unknown console type '{0}'.", + consoleType + ), + }); +} + +export function cannotLaunchBecauseSourceMaps( + programPath: string +): DebugProtocol.Message { + return new ErrorWithMessage({ + id: 2002, + format: localize( + "VSND2002", + "Cannot launch program '{0}'; configuring source maps might help.", + "{path}" + ), + variables: { path: programPath }, + }); +} + +export function cannotLaunchBecauseOutFiles( + programPath: string +): DebugProtocol.Message { + return new ErrorWithMessage({ + id: 2003, + format: localize( + "VSND2003", + "Cannot launch program '{0}'; setting the '{1}' attribute might help.", + "{path}", + "outFiles" + ), + variables: { path: programPath }, + }); +} + +export function cannotLaunchBecauseJsNotFound( + programPath: string +): DebugProtocol.Message { + return new ErrorWithMessage({ + id: 2009, + format: localize( + "VSND2009", + "Cannot launch program '{0}' because corresponding JavaScript cannot be found.", + "{path}" + ), + variables: { path: programPath }, + }); +} + +export function cannotLoadEnvVarsFromFile( + error: string +): DebugProtocol.Message { + return new ErrorWithMessage({ + id: 2029, + format: localize( + "VSND2029", + "Can't load environment variables from file ({0}).", + "{_error}" + ), + variables: { _error: error }, + }); +} diff --git a/Source/extension.ts b/Source/extension.ts new file mode 100644 index 00000000..7f35d332 --- /dev/null +++ b/Source/extension.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from "vscode"; +import * as Core from "vscode-chrome-debug-core"; + +export function activate(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.commands.registerCommand( + "extension.node-debug2.toggleSkippingFile", + toggleSkippingFile + ) + ); + context.subscriptions.push( + vscode.debug.registerDebugConfigurationProvider( + "legacy-extensionHost", + new ExtensionHostDebugConfigurationProvider() + ) + ); +} + +export function deactivate() {} + +function toggleSkippingFile(path: string | number): void { + if (!path) { + const activeEditor = vscode.window.activeTextEditor; + path = activeEditor && activeEditor.document.fileName; + } + + if (path && vscode.debug.activeDebugSession) { + const args: Core.IToggleSkipFileStatusArgs = + typeof path === "string" ? { path } : { sourceReference: path }; + vscode.debug.activeDebugSession.customRequest( + "toggleSkipFileStatus", + args + ); + } +} + +class ExtensionHostDebugConfigurationProvider + implements vscode.DebugConfigurationProvider +{ + resolveDebugConfiguration( + folder: vscode.WorkspaceFolder | undefined, + debugConfiguration: vscode.DebugConfiguration + ): vscode.ProviderResult { + return debugConfiguration; + } +} diff --git a/Source/nodeBreakpoints.ts b/Source/nodeBreakpoints.ts new file mode 100644 index 00000000..1b64557e --- /dev/null +++ b/Source/nodeBreakpoints.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { + Breakpoints, + chromeConnection, + InternalSourceBreakpoint, + ISetBreakpointResult, + ISetBreakpointsArgs, + logger, + ScriptContainer, +} from "vscode-chrome-debug-core"; +import { NodeDebugAdapter } from "./nodeDebugAdapter"; + +export class NodeBreakpoints extends Breakpoints { + constructor( + private nodeDebugAdapter: NodeDebugAdapter, + chromeConnection: chromeConnection.ChromeConnection + ) { + super(nodeDebugAdapter, chromeConnection); + } + + /** + * Override addBreakpoints, which is called by setBreakpoints to make the actual call to Chrome. + */ + protected async addBreakpoints( + url: string, + breakpoints: InternalSourceBreakpoint[], + scripts: ScriptContainer + ): Promise { + const responses = await super.addBreakpoints(url, breakpoints, scripts); + if ( + this.nodeDebugAdapter.entryPauseEvent && + !this.nodeDebugAdapter.finishedConfig + ) { + const entryLocation = + this.nodeDebugAdapter.entryPauseEvent.callFrames[0].location; + const bpAtEntryLocationIdx = responses.findIndex((response) => { + // Don't compare column location, because you can have a bp at col 0, then break at some other column + return ( + response && + response.actualLocation && + response.actualLocation.lineNumber === + entryLocation.lineNumber && + response.actualLocation.scriptId === entryLocation.scriptId + ); + }); + const bpAtEntryLocation = + bpAtEntryLocationIdx >= 0 && breakpoints[bpAtEntryLocationIdx]; + + if (bpAtEntryLocation) { + let conditionPassed = true; + if (bpAtEntryLocation.condition) { + const evalConditionResponse = + await this.nodeDebugAdapter.evaluateOnCallFrame( + bpAtEntryLocation.condition, + this.nodeDebugAdapter.entryPauseEvent.callFrames[0] + ); + conditionPassed = + !evalConditionResponse.exceptionDetails && + (!!evalConditionResponse.result.objectId || + !!evalConditionResponse.result.value); + } + + if (conditionPassed) { + // There is some initial breakpoint being set to the location where we stopped on entry, so need to pause even if + // the stopOnEntry flag is not set + logger.log( + "Got a breakpoint set in the entry location, so will stop even though stopOnEntry is not set" + ); + this.nodeDebugAdapter.continueAfterConfigDone = false; + this.nodeDebugAdapter.expectingStopReason = "breakpoint"; + } else { + logger.log( + "Breakpoint condition at entry location did not evaluate to truthy value" + ); + } + } + } + + return responses; + } + + protected validateBreakpointsPath( + args: ISetBreakpointsArgs + ): Promise { + return super.validateBreakpointsPath(args).catch((e) => { + if ( + !this.nodeDebugAdapter.launchAttachArgs.disableOptimisticBPs && + args.source.path && + this.nodeDebugAdapter.jsDeterminant.isJavaScript( + args.source.path + ) + ) { + return undefined; + } else { + return Promise.reject(e); + } + }); + } +} diff --git a/Source/nodeDebug.ts b/Source/nodeDebug.ts new file mode 100644 index 00000000..713c2c97 --- /dev/null +++ b/Source/nodeDebug.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { + ChromeDebugSession, + logger, + telemetry, +} from "vscode-chrome-debug-core"; +import * as path from "path"; +import * as os from "os"; + +import { NodeDebugAdapter } from "./nodeDebugAdapter"; +import { NodeBreakpoints } from "./nodeBreakpoints"; +import { NodeScriptContainer } from "./nodeScripts"; + +ChromeDebugSession.run( + ChromeDebugSession.getSession({ + logFilePath: path.join(os.tmpdir(), "vscode-node-debug2.txt"), // non-.txt file types can't be uploaded to github + adapter: NodeDebugAdapter, + extensionName: "node-debug2", + breakpoints: NodeBreakpoints, + scriptContainer: NodeScriptContainer, + }) +); + +/* tslint:disable:no-var-requires */ +const debugAdapterVersion = require("../../package.json").version; +logger.log("node-debug2: " + debugAdapterVersion); + +/* __GDPR__FRAGMENT__ + "DebugCommonProperties" : { + "Versions.DebugAdapter" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ +telemetry.telemetry.addCustomGlobalProperty({ + "Versions.DebugAdapter": debugAdapterVersion, +}); diff --git a/Source/nodeDebugAdapter.ts b/Source/nodeDebugAdapter.ts new file mode 100644 index 00000000..a3e73137 --- /dev/null +++ b/Source/nodeDebugAdapter.ts @@ -0,0 +1,1346 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { + ChromeDebugAdapter, + chromeUtils, + ISourceMapPathOverrides, + utils as CoreUtils, + logger, + telemetry as CoreTelemetry, + Crdp, + ChromeDebugSession, + IOnPausedResult, +} from "vscode-chrome-debug-core"; +const telemetry = CoreTelemetry.telemetry; + +import { DebugProtocol } from "vscode-debugprotocol"; +import { OutputEvent, CapabilitiesEvent, Event } from "vscode-debugadapter"; +import { ErrorWithMessage } from "vscode-chrome-debug-core/out/src/errors"; + +import * as path from "path"; +import * as fs from "fs"; +import * as cp from "child_process"; + +import { + ILaunchRequestArguments, + IAttachRequestArguments, + ICommonRequestArgs, + ILaunchVSCodeArguments, + ILaunchVSCodeArgument, +} from "./nodeDebugInterfaces"; +import * as pathUtils from "./pathUtils"; +import * as utils from "./utils"; +import * as errors from "./errors"; +import * as wsl from "./wslSupport"; + +import * as nls from "vscode-nls"; +import { FinishedStartingUpEventArguments } from "vscode-chrome-debug-core/lib/src/executionTimingsReporter"; +import { ReasonType } from "vscode-chrome-debug-core/lib/src/chrome/stoppedEvent"; +let localize = nls.loadMessageBundle(); + +const DefaultSourceMapPathOverrides: ISourceMapPathOverrides = { + "webpack:///./~/*": "${cwd}/node_modules/*", + "webpack:///./*": "${cwd}/*", + "webpack:///*": "*", + "meteor://💻app/*": "${cwd}/*", +}; + +export class ProcessEvent extends Event implements DebugProtocol.ProcessEvent { + body: { + name: string; + systemProcessId?: number; + isLocalProcess?: boolean; + startMethod?: "launch" | "attach" | "attachForSuspendedLaunch"; + pointerSize?: number; + }; + + public constructor(name: string, systemProcessId?: number) { + super("process"); + this.body = { + name, + systemProcessId, + }; + } +} +export class NodeDebugAdapter extends ChromeDebugAdapter { + private static NODE = "node"; + private static RUNINTERMINAL_TIMEOUT = 5000; + private static NODE_TERMINATION_POLL_INTERVAL = 3000; + private static DEBUG_BRK_DEP_MSG = + /\(node:\d+\) \[DEP0062\] DeprecationWarning: `node --inspect --debug-brk` is deprecated\. Please use `node --inspect-brk` instead\.\s*/; + + public static NODE_INTERNALS = ""; + + protected _launchAttachArgs: ICommonRequestArgs; + + private _jsDeterminant = new utils.JavaScriptDeterminant(); + private _loggedTargetVersion: boolean; + private _nodeProcessId: number; + private _pollForNodeProcess: boolean; + + // Flags relevant during init + private _continueAfterConfigDone = true; + private _entryPauseEvent: Crdp.Debugger.PausedEvent; + private _waitingForEntryPauseEvent = true; + private _finishedConfig = false; + private _handlingEarlyNodeMsgs = true; + private _captureFromStd: boolean = false; + + private _supportsRunInTerminalRequest: boolean; + private _restartMode: boolean; + private _isTerminated: boolean; + private _adapterID: string; + + get entryPauseEvent(): Crdp.Debugger.PausedEvent | undefined { + return this._entryPauseEvent; + } + + get jsDeterminant(): utils.JavaScriptDeterminant { + return this._jsDeterminant; + } + + get finishedConfig(): boolean { + return this._finishedConfig; + } + + get continueAfterConfigDone(): boolean { + return this._continueAfterConfigDone; + } + + set continueAfterConfigDone(v: boolean) { + this._continueAfterConfigDone = v; + } + + get expectingStopReason(): ReasonType { + return this._expectingStopReason; + } + + set expectingStopReason(v: ReasonType) { + this._expectingStopReason = v; + } + + private get nodeProcessId(): number { + return this._nodeProcessId; + } + + private set nodeProcessId(id: number) { + this._nodeProcessId = id; + + if (id !== 0) { + this.session.sendEvent(new ProcessEvent("", id)); + } + } + + get launchAttachArgs(): ICommonRequestArgs { + return this._launchAttachArgs; + } + + /** + * Returns whether this is a non-EH attach scenario + */ + private get normalAttachMode(): boolean { + return this._attachMode && !this.isExtensionHost(); + } + + private get supportsTerminateRequest(): boolean { + return process.platform !== "win32" && !this.isExtensionHost(); + } + + public initialize( + args: DebugProtocol.InitializeRequestArguments + ): DebugProtocol.Capabilities { + this._adapterID = args.adapterID; + this._promiseRejectExceptionFilterEnabled = this.isExtensionHost(); + this._supportsRunInTerminalRequest = args.supportsRunInTerminalRequest; + + if (args.locale) { + localize = nls.config({ locale: args.locale })(); + } + + const capabilities = super.initialize(args); + capabilities.supportsLogPoints = true; + capabilities.supportsTerminateRequest = this.supportsTerminateRequest; + + return capabilities; + } + + public async launch(args: ILaunchRequestArguments): Promise { + if (typeof args.enableSourceMapCaching !== "boolean") { + args.enableSourceMapCaching = this.isExtensionHost(); + } + + if ( + args.console && + args.console !== "internalConsole" && + typeof args._suppressConsoleOutput === "undefined" + ) { + args._suppressConsoleOutput = true; + } + + await super.launch(args); + if (args.__restart && typeof args.__restart.port === "number") { + return this.doAttach( + args.__restart.port, + undefined, + args.address, + args.timeout + ); + } + + const port = args.port || utils.random(3000, 50000); + + if (args.useWSL && !wsl.subsystemForLinuxPresent()) { + return Promise.reject( + new ErrorWithMessage({ + id: 2007, + format: localize( + "attribute.wsl.not.exist", + "Cannot find Windows Subsystem for Linux installation." + ), + }) + ); + } + + this._continueAfterConfigDone = !args.stopOnEntry; + + if (this.isExtensionHost()) { + return this.extensionHostLaunch(args, port); + } + + let runtimeExecutable = args.runtimeExecutable; + if (args.useWSL) { + runtimeExecutable = runtimeExecutable || NodeDebugAdapter.NODE; + } else if (runtimeExecutable) { + if (path.isAbsolute(runtimeExecutable)) { + const re = pathUtils.findExecutable( + runtimeExecutable, + args.env + ); + if (!re) { + return this.getNotExistErrorResponse( + "runtimeExecutable", + runtimeExecutable + ); + } + + runtimeExecutable = re; + } else { + const re = pathUtils.findOnPath(runtimeExecutable, args.env); + if (!re) { + return this.getRuntimeNotOnPathErrorResponse( + runtimeExecutable + ); + } + + runtimeExecutable = re; + } + } else { + const re = pathUtils.findOnPath(NodeDebugAdapter.NODE, args.env); + if (!re) { + return Promise.reject( + errors.runtimeNotFound(NodeDebugAdapter.NODE) + ); + } + + // use node from PATH + runtimeExecutable = re; + } + + let programPath = args.program; + if (programPath) { + if (!path.isAbsolute(programPath)) { + return this.getRelativePathErrorResponse( + "program", + programPath + ); + } + + if (!fs.existsSync(programPath)) { + if (fs.existsSync(programPath + ".js")) { + programPath += ".js"; + } else { + return this.getNotExistErrorResponse( + "program", + programPath + ); + } + } + + programPath = path.normalize(programPath); + if ( + pathUtils.normalizeDriveLetter(programPath) !== + pathUtils.realCasePath(programPath) + ) { + logger.warn( + localize( + "program.path.case.mismatch.warning", + "Program path uses differently cased character as file on disk; this might result in breakpoints not being hit." + ) + ); + } + } + + this._captureFromStd = args.outputCapture === "std"; + + if (args.__debuggablePatterns) { + this._jsDeterminant.updatePatterns(args.__debuggablePatterns); + } + + const resolvedProgramPath = await this.resolveProgramPath( + programPath, + args.sourceMaps + ); + let program: string; + let cwd = args.cwd; + if (cwd) { + if (!path.isAbsolute(cwd)) { + return this.getRelativePathErrorResponse("cwd", cwd); + } + + if (!fs.existsSync(cwd)) { + return this.getNotExistErrorResponse("cwd", cwd); + } + + // if working dir is given and if the executable is within that folder, we make the executable path relative to the working dir + if (resolvedProgramPath) { + program = (await pathUtils.isSymlinkedPath(cwd)) + ? resolvedProgramPath + : path.relative(cwd, resolvedProgramPath); + } + } else if (resolvedProgramPath) { + // if no working dir given, we use the direct folder of the executable + cwd = path.dirname(resolvedProgramPath); + program = (await pathUtils.isSymlinkedPath(cwd)) + ? resolvedProgramPath + : path.basename(resolvedProgramPath); + } + + const runtimeArgs = args.runtimeArgs || []; + const programArgs = args.args || []; + + const debugArgs = detectSupportedDebugArgsForLaunch( + args, + runtimeExecutable, + args.env + ); + let launchArgs = []; + if (!args.noDebug && !args.port) { + // Always stop on entry to set breakpoints + if (debugArgs === DebugArgs.Inspect_DebugBrk) { + launchArgs.push(`--inspect=${port}`); + launchArgs.push("--debug-brk"); + } else { + launchArgs.push(`--inspect-brk=${port}`); + } + } + + launchArgs = runtimeArgs.concat( + launchArgs, + program ? [program] : [], + programArgs + ); + + const wslLaunchArgs = wsl.createLaunchArg( + args.useWSL, + args.console === "externalTerminal", + cwd, + runtimeExecutable, + launchArgs, + program + ); + // if using subsystem for linux, we will trick the debugger to map source files + if (args.useWSL && !args.localRoot && !args.remoteRoot) { + this.pathTransformer.attach({ + remoteRoot: wslLaunchArgs.remoteRoot, + localRoot: wslLaunchArgs.localRoot, + }); + } + + const envArgs = this.collectEnvFileArgs(args) || args.env; + if ( + (args.console === "integratedTerminal" || + args.console === "externalTerminal") && + this._supportsRunInTerminalRequest + ) { + const termArgs: DebugProtocol.RunInTerminalRequestArguments = { + kind: + args.console === "integratedTerminal" + ? "integrated" + : "external", + title: localize("node.console.title", "Node Debug Console"), + cwd, + args: wslLaunchArgs.combined, + env: envArgs, + }; + await this.launchInTerminal(termArgs); + if (args.noDebug) { + this.terminateSession("cannot track process"); + } + } else if (!args.console || args.console === "internalConsole") { + await this.launchInInternalConsole( + wslLaunchArgs.executable, + wslLaunchArgs.args, + envArgs, + cwd + ); + } else { + throw errors.unknownConsoleType(args.console); + } + + if (!args.noDebug) { + await this.doAttach( + port, + undefined, + args.address, + args.timeout, + undefined, + args.extraCRDPChannelPort + ); + } + } + + private extensionHostLaunch( + launchArgs: ILaunchRequestArguments, + debugPort: number + ): Promise { + // Separate all "paths" from an arguments into separate attributes. + const args = launchArgs.args.map((arg) => { + if (arg.startsWith("-")) { + // arg is an option + const pair = arg.split("=", 2); + if ( + pair.length === 2 && + (fs.existsSync(pair[1]) || fs.existsSync(pair[1] + ".js")) + ) { + return { prefix: pair[0] + "=", path: pair[1] }; + } + return { prefix: arg }; + } else { + // arg is a path + try { + const stat = fs.lstatSync(arg); + if (stat.isDirectory()) { + return { prefix: "--folder-uri=", path: arg }; + } else if (stat.isFile()) { + return { prefix: "--file-uri=", path: arg }; + } + } catch (err) { + // file not found + } + return { path: arg }; // just return the path blindly and hope for the best... + } + }); + + if (!launchArgs.noDebug) { + args.unshift({ prefix: `--inspect-brk-extensions=${debugPort}` }); + } + + args.unshift({ prefix: `--debugId=${launchArgs.__sessionId}` }); // pass the debug session ID so that broadcast events know where they come from + + const launchVSCodeArgs: ILaunchVSCodeArguments = { + args: args, + env: this.collectEnvFileArgs(launchArgs) || launchArgs.env, + }; + + return new Promise((resolve, reject) => { + this._session.sendRequest( + "launchVSCode", + launchVSCodeArgs, + NodeDebugAdapter.RUNINTERMINAL_TIMEOUT, + (response) => { + if (response.success) { + if ( + response.body && + typeof response.body.processId === "number" + ) { + this.nodeProcessId = response.body.processId; + } + resolve(); + } else { + reject(errors.cannotDebugExtension(response.message)); + this.terminateSession( + "launchVSCode error: " + response.message + ); + } + } + ); + }); + } + + public async attach(args: IAttachRequestArguments): Promise { + try { + if (typeof args.enableSourceMapCaching !== "boolean") { + args.enableSourceMapCaching = true; + } + + return super.attach(args); + } catch (err) { + if ( + err.format && + err.format.indexOf("Cannot connect to runtime process") >= 0 + ) { + // hack -core error msg + err.format = + "Ensure Node was launched with --inspect. " + err.format; + } + + throw err; + } + } + + protected commonArgs(args: ICommonRequestArgs): void { + args.sourceMapPathOverrides = getSourceMapPathOverrides( + args.cwd, + args.sourceMapPathOverrides + ); + fixNodeInternalsSkipFiles(args); + + args.smartStep = + typeof args.smartStep === "undefined" + ? !this._isVSClient + : args.smartStep; + + this._restartMode = args.restart; + super.commonArgs(args); + } + + protected hookConnectionEvents(): void { + super.hookConnectionEvents(); + + this.chrome.Runtime.on("executionContextDestroyed", (params) => { + if (params.executionContextId === 1) { + this.terminateSession("Program ended"); + } + }); + } + + protected async doAttach( + port: number, + targetUrl?: string, + address?: string, + timeout?: number, + websocketUrl?: string, + extraCRDPChannelPort?: number + ): Promise { + await super.doAttach( + port, + targetUrl, + address, + timeout, + websocketUrl, + extraCRDPChannelPort + ); + this.beginWaitingForDebuggerPaused(); + this.getNodeProcessDetailsIfNeeded(); + + this._session.sendEvent( + new CapabilitiesEvent({ supportsStepBack: this.supportsStepBack() }) + ); + } + + private supportsStepBack(): boolean { + return this._domains.has("TimeTravel"); + } + + private launchInTerminal( + termArgs: DebugProtocol.RunInTerminalRequestArguments + ): Promise { + return new Promise((resolve, reject) => { + this._session.sendRequest( + "runInTerminal", + termArgs, + NodeDebugAdapter.RUNINTERMINAL_TIMEOUT, + (response) => { + if (response.success) { + // since node starts in a terminal, we cannot track it with an 'exit' handler + // plan for polling after we have gotten the process pid. + this._pollForNodeProcess = true; + resolve(); + } else { + reject(errors.cannotLaunchInTerminal(response.message)); + this.terminateSession( + "terminal error: " + response.message + ); + } + } + ); + }); + } + + private launchInInternalConsole( + runtimeExecutable: string, + launchArgs: string[], + envArgs?: any, + cwd?: string + ): Promise { + // merge environment variables into a copy of the process.env + const env = Object.assign({}, process.env, envArgs); + Object.keys(env) + .filter((k) => env[k] === null) + .forEach((key) => delete env[key]); + + const spawnOpts: cp.SpawnOptions = { cwd, env }; + + // Workaround for bug Microsoft/vscode#45832 + if ( + process.platform === "win32" && + runtimeExecutable.indexOf(" ") > 0 + ) { + let foundArgWithSpace = false; + + // check whether there is one arg with a space + const args: string[] = []; + for (const a of launchArgs) { + if (a.indexOf(" ") > 0) { + args.push(`"${a}"`); + foundArgWithSpace = true; + } else { + args.push(a); + } + } + + if (foundArgWithSpace) { + launchArgs = args; + runtimeExecutable = `"${runtimeExecutable}"`; + spawnOpts.shell = true; + } + } + + this.logLaunchCommand(runtimeExecutable, launchArgs); + spawnOpts.detached = this.supportsTerminateRequest; // https://github.com/Microsoft/vscode/issues/57018 + const nodeProcess = cp.spawn(runtimeExecutable, launchArgs, spawnOpts); + return new Promise((resolve, reject) => { + this.nodeProcessId = nodeProcess.pid; + nodeProcess.on("error", (error) => { + reject(errors.cannotLaunchDebugTarget(errors.toString())); + const msg = `Node process error: ${error}`; + logger.error(msg); + this.terminateSession(msg); + }); + nodeProcess.on("exit", () => { + const msg = "Target exited"; + logger.log(msg); + if (!this.isExtensionHost()) { + this.terminateSession(msg); + } + }); + nodeProcess.on("close", (code) => { + const msg = "Target closed"; + logger.log(msg); + if (!this.isExtensionHost()) { + this.terminateSession(msg); + } + }); + + const noDebugMode = (( + this._launchAttachArgs + )).noDebug; + + this.captureStderr(nodeProcess, noDebugMode); + + // Must attach a listener to stdout or process will hang on Windows + nodeProcess.stdout.on("data", (data: string) => { + if ( + (noDebugMode || this._captureFromStd) && + !this._launchAttachArgs._suppressConsoleOutput + ) { + let msg = data.toString(); + this._session.sendEvent(new OutputEvent(msg, "stdout")); + } + }); + + resolve(); + }); + } + + private captureStderr( + nodeProcess: cp.ChildProcess, + noDebugMode: boolean + ): void { + nodeProcess.stderr.on("data", (data: string) => { + let msg = data.toString(); + let isLastEarlyNodeMsg = false; + + // We want to send initial stderr output back to the console because they can contain useful errors. + // But there are some messages printed to stderr at the start of debugging that can be misleading. + // Node is "handlingEarlyNodeMsgs" from launch to when one of these messages is printed: + // "To start debugging, open the following URL in Chrome: ..." - Node <8 + // --debug-brk deprecation message - Node 8+ + // In this mode, we strip those messages from stderr output. After one of them is printed, we don't + // watch stderr anymore and pass it along (unless in noDebugMode). + if (this._handlingEarlyNodeMsgs && !noDebugMode) { + const chromeMsgIndex = msg.indexOf( + "To start debugging, open the following URL in Chrome:" + ); + if (chromeMsgIndex >= 0) { + msg = msg.substr(0, chromeMsgIndex); + isLastEarlyNodeMsg = true; + } + + const msgMatch = msg.match(NodeDebugAdapter.DEBUG_BRK_DEP_MSG); + if (msgMatch) { + isLastEarlyNodeMsg = true; + msg = msg.replace(NodeDebugAdapter.DEBUG_BRK_DEP_MSG, ""); + } + + const helpMsg = + /For help see https:\/\/nodejs.org\/en\/docs\/inspector\s*/; + msg = msg.replace(helpMsg, ""); + } + + if ( + (this._handlingEarlyNodeMsgs || + noDebugMode || + this._captureFromStd) && + !this._launchAttachArgs._suppressConsoleOutput + ) { + this._session.sendEvent(new OutputEvent(msg, "stderr")); + } + + if (isLastEarlyNodeMsg) { + this._handlingEarlyNodeMsgs = false; + } + }); + } + + protected onConsoleAPICalled( + params: Crdp.Runtime.ConsoleAPICalledEvent + ): void { + // Once any console API message is received, we are done listening to initial stderr output + this._handlingEarlyNodeMsgs = false; + + if (this._captureFromStd) { + return; + } + + // Strip the --debug-brk deprecation message which is printed at startup + if ( + !params.args || + params.args.length !== 1 || + typeof params.args[0].value !== "string" || + !params.args[0].value.match(NodeDebugAdapter.DEBUG_BRK_DEP_MSG) + ) { + super.onConsoleAPICalled(params); + } + } + + private collectEnvFileArgs(args: ILaunchRequestArguments): any { + // read env from disk and merge into envVars + if (args.envFile) { + try { + const env = {}; + const buffer = utils.stripBOM( + fs.readFileSync(args.envFile, "utf8") + ); + buffer.split("\n").forEach((line) => { + const r = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/); + if (r !== null) { + const key = r[1]; + if (!process.env[key]) { + // .env variables never overwrite existing variables (see #21169) + let value = r[2] || ""; + if ( + value.length > 0 && + value.charAt(0) === '"' && + value.charAt(value.length - 1) === '"' + ) { + value = value.replace(/\\n/gm, "\n"); + } + env[key] = value.replace(/(^['"]|['"]$)/g, ""); + } + } + }); + + return utils.extendObject(env, args.env); // launch config env vars overwrite .env vars + } catch (e) { + throw errors.cannotLoadEnvVarsFromFile(e.message); + } + } + } + + /** + * Override so that -core's call on attach will be ignored, and we can wait until the first break when ready to set BPs. + */ + protected async sendInitializedEvent(): Promise { + if (!this._waitingForEntryPauseEvent) { + return super.sendInitializedEvent(); + } + } + + public async configurationDone(): Promise { + if (!this.chrome) { + // It's possible to get this request after we've detached, see #21973 + return super.configurationDone(); + } + + await this._breakpoints.breakpointsQueueDrained; + + // This message means that all breakpoints have been set by the client. We should be paused at this point. + // So tell the target to continue, or tell the client that we paused, as needed + this._finishedConfig = true; + if (this._continueAfterConfigDone) { + this._expectingStopReason = undefined; + await this.continue(/*internal=*/ true); + } else if (this._entryPauseEvent) { + await this.onPaused(this._entryPauseEvent); + } + + this.events.emit(ChromeDebugSession.FinishedStartingUpEventName, { + requestedContentWasDetected: true, + } as FinishedStartingUpEventArguments); + await super.configurationDone(); + } + + private killNodeProcess(): void { + if (this.nodeProcessId && !this.normalAttachMode) { + if (this.nodeProcessId === 1) { + logger.log("Not killing launched process. It has PID=1"); + } else { + logger.log("Killing process with id: " + this.nodeProcessId); + utils.killTree(this.nodeProcessId); + } + + this.nodeProcessId = 0; + } + } + + public async terminate( + args: DebugProtocol.TerminateArguments + ): Promise { + this._clientRequestedSessionEnd = true; + if ( + !this._attachMode && + !(this._launchAttachArgs).useWSL && + this.nodeProcessId > 0 + ) { + // -pid to kill the process group + // https://github.com/Microsoft/vscode/issues/57018 + const groupPID = -this.nodeProcessId; + + try { + logger.log(`Sending SIGINT to ${groupPID}`); + process.kill(groupPID, "SIGINT"); + } catch (e) { + if (e.message === "kill ESRCH") { + logger.log( + `Got 'kill ESRCH'. Sending SIGINT to ${this.nodeProcessId}` + ); + process.kill(this.nodeProcessId, "SIGINT"); + } + } + } + } + + public async terminateSession( + reason: string, + args?: DebugProtocol.DisconnectArguments + ): Promise { + if ( + this.isExtensionHost() && + args && + typeof args.restart === "boolean" && + args.restart + ) { + this.nodeProcessId = 0; + } else if (this._restartMode && !args) { + // If restart: true, only kill the process when the client has disconnected. 'args' present implies that a Disconnect request was received + this.nodeProcessId = 0; + } + + this.killNodeProcess(); + const restartArgs = + this._restartMode && !this._clientRequestedSessionEnd + ? { port: this._port } + : undefined; + return super.terminateSession(reason, undefined, restartArgs); + } + + protected async onPaused( + notification: Crdp.Debugger.PausedEvent, + expectingStopReason = this._expectingStopReason + ): Promise { + // If we don't have the entry location, this must be the entry pause + if (this._waitingForEntryPauseEvent) { + logger.log(Date.now() / 1000 + ": Paused on entry"); + this._expectingStopReason = "entry"; + this._entryPauseEvent = notification; + this._waitingForEntryPauseEvent = false; + + if ( + (this.normalAttachMode && + this._launchAttachArgs.stopOnEntry !== false) || + (this.isExtensionHost() && this._launchAttachArgs.stopOnEntry) + ) { + // In attach mode, and we did pause right away, so assume --debug-brk was set and we should show paused. + // In normal attach mode, assume stopOnEntry unless explicitly disabled. + // In extensionhost mode, only when stopOnEntry is explicitly enabled + this._continueAfterConfigDone = false; + } + + await this.getNodeProcessDetailsIfNeeded(); + await this.sendInitializedEvent(); + return { didPause: true }; + } else { + return super.onPaused(notification, expectingStopReason); + } + } + + private async resolveProgramPath( + programPath: string, + sourceMaps: boolean + ): Promise { + logger.verbose(`Launch: Resolving programPath: ${programPath}`); + if (!programPath) { + return programPath; + } + + if (this.jsDeterminant.isJavaScript(programPath)) { + if (!sourceMaps) { + return programPath; + } + + // if programPath is a JavaScript file and sourceMaps are enabled, we don't know whether + // programPath is the generated file or whether it is the source (and we need source mapping). + // Typically this happens if a tool like 'babel' or 'uglify' is used (because they both transpile js to js). + // We use the source maps to find a 'source' file for the given js file. + const generatedPath = + await this.sourceMapTransformer.getGeneratedPathFromAuthoredPath( + programPath + ); + if (generatedPath && generatedPath !== programPath) { + // programPath must be source because there seems to be a generated file for it + logger.log( + `Launch: program '${programPath}' seems to be the source; launch the generated file '${generatedPath}' instead` + ); + programPath = generatedPath; + } else { + logger.log( + `Launch: program '${programPath}' seems to be the generated file` + ); + } + + return programPath; + } else { + // node cannot execute the program directly + if (!sourceMaps) { + return Promise.reject( + errors.cannotLaunchBecauseSourceMaps(programPath) + ); + } + + const generatedPath = + await this.sourceMapTransformer.getGeneratedPathFromAuthoredPath( + programPath + ); + if (!generatedPath) { + // cannot find generated file + if ( + this._launchAttachArgs.outFiles || + this._launchAttachArgs.outDir + ) { + return Promise.reject( + errors.cannotLaunchBecauseJsNotFound(programPath) + ); + } else { + return Promise.reject( + errors.cannotLaunchBecauseOutFiles(programPath) + ); + } + } + + logger.log( + `Launch: program '${programPath}' seems to be the source; launch the generated file '${generatedPath}' instead` + ); + return generatedPath; + } + } + + /** + * Wait 500-5000ms for the entry pause event, and if it doesn't come, move on with life. + * During attach, we don't know whether it's paused when attaching. + */ + private beginWaitingForDebuggerPaused(): void { + const checkPausedInterval = 50; + const timeout = this._launchAttachArgs.timeout; + + // Wait longer in launch mode - it definitely should be paused. + let count = this.normalAttachMode + ? 10 + : typeof timeout === "number" + ? Math.floor(timeout / checkPausedInterval) + : 100; + logger.log(Date.now() / 1000 + ": Waiting for initial debugger pause"); + const id = setInterval(() => { + if (this._entryPauseEvent || this._isTerminated) { + // Got the entry pause, stop waiting + clearInterval(id); + } else if (--count <= 0) { + // No entry event, so fake it and continue + logger.log( + Date.now() / 1000 + + ": Did not get a pause event after starting, so continuing" + ); + clearInterval(id); + this._continueAfterConfigDone = false; + this._waitingForEntryPauseEvent = false; + + this.getNodeProcessDetailsIfNeeded().then(() => + this.sendInitializedEvent() + ); + } + }, checkPausedInterval); + } + + protected threadName(): string { + return `Node (${this.nodeProcessId})`; + } + + private async getNodeProcessDetailsIfNeeded(): Promise { + if (this._loggedTargetVersion || !this.chrome) { + return Promise.resolve(); + } + + const response = await this.chrome.Runtime.evaluate({ + expression: "[process.pid, process.version, process.arch]", + returnByValue: true, + contextId: 1, + }).catch((error) => + logger.error("Error evaluating `process.pid`: " + error.message) + ); + + if (!response) { + return; + } + + if (this._loggedTargetVersion) { + // Possible to get two of these requests going simultaneously + return; + } + + if (response.exceptionDetails) { + const description = chromeUtils.errorMessageFromExceptionDetails( + response.exceptionDetails + ); + if ( + description.startsWith("ReferenceError: process is not defined") + ) { + logger.verbose( + "Got expected exception: `process is not defined`. Will try again later." + ); + } else { + logger.log( + "Exception evaluating `process.pid`: " + + description + + ". Will try again later." + ); + } + } else { + const [pid, version, arch] = response.result.value; + if (typeof pid !== "number") { + logger.log( + `Node returned a pid of ${pid}. Will try again later.` + ); + return; + } + + if (!this.nodeProcessId) { + this.nodeProcessId = pid; + } + + if (this._pollForNodeProcess) { + this.startPollingForNodeTermination(); + } + + this._loggedTargetVersion = true; + logger.log(`Target node version: ${version} ${arch}`); + /* __GDPR__ + "nodeVersion" : { + "version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "${include}": [ "${DebugCommonProperties}" ] + } + */ + telemetry.reportEvent("nodeVersion", { version }); + + /* __GDPR__FRAGMENT__ + "DebugCommonProperties" : { + "Versions.Target.Version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + telemetry.addCustomGlobalProperty({ + "Versions.Target.Version": version, + }); + } + } + + private startPollingForNodeTermination(): void { + const intervalId = setInterval(() => { + try { + if (this.nodeProcessId) { + // kill with signal=0 just test for whether the proc is alive. It throws if not. + process.kill(this.nodeProcessId, 0); + } else { + clearInterval(intervalId); + } + } catch (e) { + clearInterval(intervalId); + logger.log("Target process died"); + this.terminateSession("Target process died"); + } + }, NodeDebugAdapter.NODE_TERMINATION_POLL_INTERVAL); + } + + private logLaunchCommand(executable: string, args: string[]) { + // print the command to launch the target to the debug console + let cli = executable + " "; + for (let a of args) { + if (a.indexOf(" ") >= 0) { + cli += "'" + a + "'"; + } else { + cli += a; + } + cli += " "; + } + + logger.warn(cli); + } + + protected globalEvaluate( + args: Crdp.Runtime.EvaluateRequest + ): Promise { + // contextId: 1 - see https://github.com/nodejs/node/issues/8426 + if (!args.contextId) args.contextId = 1; + + return super.globalEvaluate(args); + } + + /** + * 'Path does not exist' error + */ + private getNotExistErrorResponse( + attribute: string, + path: string + ): Promise { + return Promise.reject( + new ErrorWithMessage({ + id: 2007, + format: localize( + "attribute.path.not.exist", + "Attribute '{0}' does not exist ('{1}').", + attribute, + "{path}" + ), + variables: { path }, + }) + ); + } + + /** + * 'Path not absolute' error with 'More Information' link. + */ + private getRelativePathErrorResponse( + attribute: string, + path: string + ): Promise { + const format = localize( + "attribute.path.not.absolute", + "Attribute '{0}' is not absolute ('{1}'); consider adding '{2}' as a prefix to make it absolute.", + attribute, + "{path}", + "${workspaceFolder}/" + ); + return this.getErrorResponseWithInfoLink(2008, format, { path }, 20003); + } + + private getRuntimeNotOnPathErrorResponse(runtime: string): Promise { + return Promise.reject( + new ErrorWithMessage({ + id: 2001, + format: localize( + "VSND2001", + "Cannot find runtime '{0}' on PATH. Make sure to have '{0}' installed.", + "{_runtime}" + ), + variables: { _runtime: runtime }, + }) + ); + } + + /** + * Send error response with 'More Information' link. + */ + private getErrorResponseWithInfoLink( + code: number, + format: string, + variables: any, + infoId: number + ): Promise { + return Promise.reject( + new ErrorWithMessage({ + id: code, + format, + variables, + showUser: true, + url: + "http://go.microsoft.com/fwlink/?linkID=534832#_" + + infoId.toString(), + urlLabel: localize("more.information", "More Information"), + }) + ); + } + + protected getReadonlyOrigin(aPath: string): string { + return path.isAbsolute(aPath) || + aPath.startsWith(ChromeDebugAdapter.EVAL_NAME_PREFIX) + ? localize("origin.from.node", "read-only content from Node.js") + : localize("origin.core.module", "read-only core module"); + } + + private isExtensionHost(): boolean { + return ( + this._adapterID === "extensionHost2" || + this._adapterID === "legacy-extensionHost" + ); + } +} + +function getSourceMapPathOverrides( + cwd: string, + sourceMapPathOverrides?: ISourceMapPathOverrides +): ISourceMapPathOverrides { + return sourceMapPathOverrides + ? resolveCwdPattern( + cwd, + sourceMapPathOverrides, + /*warnOnMissing=*/ true + ) + : resolveCwdPattern( + cwd, + DefaultSourceMapPathOverrides, + /*warnOnMissing=*/ false + ); +} + +function fixNodeInternalsSkipFiles(args: ICommonRequestArgs): void { + if (args.skipFiles) { + args.skipFileRegExps = args.skipFileRegExps || []; + args.skipFiles = args.skipFiles.filter((pattern) => { + const fixed = fixNodeInternalsSkipFilePattern(pattern); + if (fixed) { + args.skipFileRegExps.push(fixed); + return false; + } else { + return true; + } + }); + } +} + +const internalsRegex = new RegExp(`^${NodeDebugAdapter.NODE_INTERNALS}/(.*)`); +function fixNodeInternalsSkipFilePattern(pattern: string): string { + const internalsMatch = pattern.match(internalsRegex); + if (internalsMatch) { + return `^(?!\/)(?![a-zA-Z]:)(?!file:///)${CoreUtils.pathGlobToBlackboxedRegex( + internalsMatch[1] + )}`; + } else { + return null; + } +} + +/** + * Returns a copy of sourceMapPathOverrides with the ${cwd} pattern resolved in all entries. + */ +function resolveCwdPattern( + cwd: string, + sourceMapPathOverrides: ISourceMapPathOverrides, + warnOnMissing: boolean +): ISourceMapPathOverrides { + const resolvedOverrides: ISourceMapPathOverrides = {}; + for (let pattern in sourceMapPathOverrides) { + const replacePattern = sourceMapPathOverrides[pattern]; + resolvedOverrides[pattern] = replacePattern; + + const cwdIndex = replacePattern.indexOf("${cwd}"); + if (cwdIndex === 0) { + if (cwd) { + resolvedOverrides[pattern] = replacePattern.replace( + "${cwd}", + cwd + ); + } else if (warnOnMissing) { + logger.log( + "Warning: sourceMapPathOverrides entry contains ${cwd}, but cwd is not set" + ); + } + } else if (cwdIndex > 0) { + logger.log( + "Warning: in a sourceMapPathOverrides entry, ${cwd} is only valid at the beginning of the path" + ); + } + } + + return resolvedOverrides; +} + +export enum DebugArgs { + InspectBrk, + Inspect_DebugBrk, +} + +const defaultDebugArgs = DebugArgs.InspectBrk; +function detectSupportedDebugArgsForLaunch( + config: ILaunchRequestArguments, + runtimeExecutable: string, + env: any +): DebugArgs { + if ( + config.__nodeVersion || + (config.runtimeVersion && config.runtimeVersion !== "default") + ) { + return getSupportedDebugArgsForVersion( + config.__nodeVersion || config.runtimeVersion + ); + } else if (config.runtimeExecutable) { + logger.log("Using --inspect-brk because a runtimeExecutable is set"); + return defaultDebugArgs; + } else { + // only determine version if no runtimeExecutable is set (and 'node' on PATH is used) + logger.log( + "Spawning `node --version` to determine supported debug args" + ); + let result: cp.SpawnSyncReturns; + try { + result = cp.spawnSync(runtimeExecutable, ["--version"]); + } catch (e) { + logger.error("Node version detection failed: " + (e && e.message)); + } + + const semVerString = result.stdout + ? result.stdout.toString().trim() + : undefined; + if (semVerString) { + return getSupportedDebugArgsForVersion(semVerString); + } else { + logger.log( + "Using --inspect-brk because we couldn't get a version from node" + ); + return defaultDebugArgs; + } + } +} + +function getSupportedDebugArgsForVersion(semVerString): DebugArgs { + if (utils.compareSemver(semVerString, "v7.6.0") >= 0) { + logger.log( + `Using --inspect-brk, Node version ${semVerString} detected` + ); + return DebugArgs.InspectBrk; + } else { + logger.log( + `Using --inspect --debug-brk, Node version ${semVerString} detected` + ); + return DebugArgs.Inspect_DebugBrk; + } +} diff --git a/Source/nodeDebugInterfaces.d.ts b/Source/nodeDebugInterfaces.d.ts new file mode 100644 index 00000000..d2baa38a --- /dev/null +++ b/Source/nodeDebugInterfaces.d.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { DebugProtocol } from "vscode-debugprotocol"; +import * as Core from "vscode-chrome-debug-core"; + +type ConsoleType = + | "internalConsole" + | "integratedTerminal" + | "externalTerminal"; + +type OutputCaptureType = "console" | "std"; + +export interface ICommonRequestArgs extends Core.ICommonRequestArgs { + stopOnEntry?: boolean; + address?: string; + timeout?: number; + /** Optional cwd for sourceMapPathOverrides resolution */ + cwd?: string; + /** Request frontend to restart session on termination. */ + restart?: boolean; + + /** Don't set breakpoints in JS files that don't have sourcemaps */ + disableOptimisticBPs?: boolean; +} + +/** + * This interface should always match the schema found in the node-debug extension manifest. + */ +export interface ILaunchRequestArguments + extends Core.ILaunchRequestArgs, + ICommonRequestArgs { + /** An absolute path to the program to debug. */ + program: string; + /** Optional arguments passed to the debuggee. */ + args?: string[]; + /** Launch the debuggee in this working directory (specified as an absolute path). If omitted the debuggee is lauched in its own directory. */ + cwd: string; + /** Absolute path to the runtime executable to be used. Default is the runtime executable on the PATH. */ + runtimeExecutable?: string; + /** Optional arguments passed to the runtime executable. */ + runtimeArgs?: string[]; + /** Optional environment variables to pass to the debuggee. The string valued properties of the 'environmentVariables' are used as key/value pairs. */ + env?: { [key: string]: string | null }; + envFile?: string; + /** Where to launch the debug target. */ + console?: ConsoleType; + /** Manually selected debugging port */ + port?: number; + /** Source of the debug output */ + outputCapture?: OutputCaptureType; + /** Use Windows Subsystem for Linux */ + useWSL?: boolean; + runtimeVersion?: string; + + /** Logging options */ + diagnosticLogging?: boolean; + verboseDiagnosticLogging?: boolean; + + // extensionHost option + __sessionId?: string; + + // When node version is detected by node-debug + __nodeVersion?: string; + + // A list of glob patterns that can be debugged by the extension. + __debuggablePatterns: string[]; +} + +/** + * This interface should always match the schema found in the node-debug extension manifest. + */ +export interface IAttachRequestArguments + extends Core.IAttachRequestArgs, + ICommonRequestArgs { + /** Node's root directory. */ + remoteRoot?: string; + /** VS Code's root directory. */ + localRoot?: string; + /** Send a USR1 signal to this process. */ + processId?: string; +} + +/** + * This interface represents a single command line argument split into a "prefix" and a "path" half. + * The optional "prefix" contains arbitrary text and the optional "path" contains a file system path. + * Concatenating both results in the original command line argument. + */ +export interface ILaunchVSCodeArgument { + prefix?: string; + path?: string; +} + +export interface ILaunchVSCodeArguments { + args: ILaunchVSCodeArgument[]; + env?: { [key: string]: string | null }; +} + +export type NodeDebugError = DebugProtocol.Message & Error; diff --git a/Source/nodeScripts.ts b/Source/nodeScripts.ts new file mode 100644 index 00000000..e4c09b17 --- /dev/null +++ b/Source/nodeScripts.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { ScriptContainer } from "vscode-chrome-debug-core"; +import { NodeDebugAdapter } from "./nodeDebugAdapter"; +import * as path from "path"; + +export class NodeScriptContainer extends ScriptContainer { + /** + * If realPath is an absolute path or a URL, return realPath. Otherwise, prepend the node_internals marker + */ + public realPathToDisplayPath(realPath: string): string { + if (!realPath.match(/VM\d+/) && !path.isAbsolute(realPath)) { + return `${NodeDebugAdapter.NODE_INTERNALS}/${realPath}`; + } + + return super.realPathToDisplayPath(realPath); + } + + /** + * If displayPath starts with the NODE_INTERNALS indicator, strip it. + */ + public displayPathToRealPath(displayPath: string): string { + const match = displayPath.match( + new RegExp(`^${NodeDebugAdapter.NODE_INTERNALS}[\\\\/](.*)`) + ); + return match ? match[1] : super.displayPathToRealPath(displayPath); + } +} diff --git a/Source/pathUtils.ts b/Source/pathUtils.ts new file mode 100644 index 00000000..f0d30c34 --- /dev/null +++ b/Source/pathUtils.ts @@ -0,0 +1,305 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as Path from "path"; +import * as FS from "fs"; +import * as CP from "child_process"; + +/** + * The input paths must use the path syntax of the underlying operating system. + */ +export function makePathAbsolute(absPath: string, relPath: string): string { + return Path.resolve(Path.dirname(absPath), relPath); +} + +/** + * Remove the first segment of the given path and return the result. + * The input path must use the path syntax of the underlying operating system. + */ +export function removeFirstSegment(path: string): string { + const segments = path.split(Path.sep); + segments.shift(); + if (segments.length > 0) { + return segments.join(Path.sep); + } + return null; +} + +/** + * Return the relative path between 'path' and 'target'. + * The input paths must use the path syntax of the underlying operating system. + */ +export function makeRelative(target: string, path: string): string { + const t = target.split(Path.sep); + const p = path.split(Path.sep); + + let i = 0; + for (; i < Math.min(t.length, p.length) && t[i] === p[i]; i++) {} + + let result = ""; + for (; i < p.length; i++) { + result = Path.join(result, p[i]); + } + return result; +} + +/** + * Returns a path with a lower case drive letter. + */ +export function normalizeDriveLetter(path: string): string { + const regex = /^([A-Z])(\:[\\\/].*)$/; + if (regex.test(path)) { + path = path.replace(regex, (s, s1, s2) => s1.toLowerCase() + s2); + } + return path; +} + +export function pathCompare(path1: string, path2: string): boolean { + return normalizeDriveLetter(path1) === normalizeDriveLetter(path2); +} + +/** + * Given an absolute, normalized, and existing file path 'realPath' returns the exact path that the file has on disk. + * On a case insensitive file system, the returned path might differ from the original path by character casing. + * On a case sensitive file system, the returned path will always be identical to the original path. + * In case of errors, null is returned. But you cannot use this function to verify that a path exists. + * realPath does not handle '..' or '.' path segments and it does not take the locale into account. + * Since a drive letter of a Windows path cannot be looked up, realPath normalizes the drive letter to lower case. + */ +export function realCasePath(path: string): string { + let dir = Path.dirname(path); + if (path === dir) { + // end recursion + // is this an upper case drive letter? + if (/^[A-Z]\:\\$/.test(path)) { + path = path.toLowerCase(); + } + return path; + } + let name = Path.basename(path).toLowerCase(); + try { + let entries = FS.readdirSync(dir); + let found = entries.filter((e) => e.toLowerCase() === name); // use a case insensitive search + if (found.length === 1) { + // on a case sensitive filesystem we cannot determine here, whether the file exists or not, hence we need the 'file exists' precondition + let prefix = realCasePath(dir); // recurse + if (prefix) { + return Path.join(prefix, found[0]); + } + } else if (found.length > 1) { + // must be a case sensitive $filesystem + const ix = found.indexOf(name); + if (ix >= 0) { + // case sensitive + let prefix = realCasePath(dir); // recurse + if (prefix) { + return Path.join(prefix, found[ix]); + } + } + } + } catch (error) { + // silently ignore error + } + return null; +} + +export function isSymlinkedPath(path: string): Promise { + return new Promise((resolve, reject) => { + FS.lstat(path, (err, stats) => { + if (err) { + reject(err); + } + + if (stats.isSymbolicLink()) { + resolve(true); + } else { + const parent = Path.dirname(path); + if (parent === path) { + resolve(false); + } else { + resolve(isSymlinkedPath(parent)); + } + } + }); + }); +} + +/** + * Make sure that all directories of the given path exist (like mkdir -p). + */ +export function mkdirs(path: string) { + if (!FS.existsSync(path)) { + mkdirs(Path.dirname(path)); + FS.mkdirSync(path); + } +} + +// ---- the following functions work with Windows and Unix-style paths independent from the underlying OS. + +/** + * Returns true if the Windows or Unix-style path is absolute. + */ +export function isAbsolutePath(path: string) { + if (path) { + if (path.charAt(0) === "/") { + return true; + } + if (/^[a-zA-Z]\:[\\\/]/.test(path)) { + return true; + } + } + return false; +} + +/** + * Convert the given Windows or Unix-style path into a normalized path that only uses forward slashes and has all superflous '..' sequences removed. + * If the path starts with a Windows-style drive letter, a '/' is prepended. + */ +export function normalize(path: string): string { + path = path.replace(/\\/g, "/"); + if (/^[a-zA-Z]\:\//.test(path)) { + path = "/" + path; + } + path = Path.normalize(path); // use node's normalize to remove '/..' etc. + path = path.replace(/\\/g, "/"); + return path; +} + +/** + * Convert the given normalized path into a Windows-style path. + */ +export function toWindows(path: string): string { + if (/^\/[a-zA-Z]\:\//.test(path)) { + path = path.substr(1); + } + path = path.replace(/\//g, "\\"); + return path; +} + +/** + * Append the given relative path to the absolute path and normalize the result. + */ +export function join(absPath: string, relPath: string): string { + absPath = normalize(absPath); + relPath = normalize(relPath); + if (absPath.charAt(absPath.length - 1) === "/") { + absPath = absPath + relPath; + } else { + absPath = absPath + "/" + relPath; + } + absPath = Path.normalize(absPath); + absPath = absPath.replace(/\\/g, "/"); + return absPath; +} + +/** + * Return the relative path between 'from' and 'to'. + */ +export function makeRelative2(from: string, to: string): string { + from = normalize(from); + to = normalize(to); + + const froms = from.substr(1).split("/"); + const tos = to.substr(1).split("/"); + + while (froms.length > 0 && tos.length > 0 && froms[0] === tos[0]) { + froms.shift(); + tos.shift(); + } + + let l = froms.length - tos.length; + if (l === 0) { + l = tos.length - 1; + } + + while (l > 0) { + tos.unshift(".."); + l--; + } + return tos.join("/"); +} + +/* + * Lookup the given program on the PATH and return its absolute path on success and undefined otherwise. + */ +export function findOnPath(program: string, args_env: any): string | undefined { + const env = extendObject(extendObject({}, process.env), args_env); + + let locator: string; + if (process.platform === "win32") { + const windir = env["WINDIR"] || "C:\\Windows"; + locator = Path.join(windir, "System32", "where.exe"); + } else { + locator = "/usr/bin/which"; + } + + try { + if (FS.existsSync(locator)) { + const lines = CP.execSync(`${locator} ${program}`, { env }) + .toString() + .split(/\r?\n/); + if (process.platform === "win32") { + // return the first path that has a executable extension + const executableExtensions = env["PATHEXT"].toUpperCase(); + for (const path of lines) { + const ext = Path.extname(path).toUpperCase(); + if (ext && executableExtensions.indexOf(ext + ";") > 0) { + return path; + } + } + } else { + // return the first path + if (lines.length > 0) { + return lines[0]; + } + } + + return undefined; + } else { + // do not report failure if 'locator' app doesn't exist + } + return program; + } catch (err) { + // fall through + } + + // fail + return undefined; +} + +export function findExecutable( + program: string, + args_env: any +): string | undefined { + const env = extendObject(extendObject({}, process.env), args_env); + + if (process.platform === "win32" && !Path.extname(program)) { + const PATHEXT = env["PATHEXT"]; + if (PATHEXT) { + const executableExtensions = PATHEXT.split(";"); + for (const extension of executableExtensions) { + const path = program + extension; + if (FS.existsSync(path)) { + return path; + } + } + } + } + + if (FS.existsSync(program)) { + return program; + } + + return undefined; +} + +export function extendObject(toObject: T, fromObject: T): T { + for (let key in fromObject) { + if (fromObject.hasOwnProperty(key)) { + toObject[key] = fromObject[key]; + } + } + return toObject; +} diff --git a/Source/terminateProcess.sh b/Source/terminateProcess.sh new file mode 100644 index 00000000..9b068843 --- /dev/null +++ b/Source/terminateProcess.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +terminateTree() { + for cpid in $(/usr/bin/pgrep -P $1); do + terminateTree $cpid + done + kill -9 $1 > /dev/null 2>&1 +} + +for pid in $*; do + terminateTree $pid +done diff --git a/Source/utils.ts b/Source/utils.ts new file mode 100644 index 00000000..df17074e --- /dev/null +++ b/Source/utils.ts @@ -0,0 +1,115 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as path from "path"; +import * as fs from "fs"; +import * as cp from "child_process"; +import * as match from "minimatch"; + +const NODE_SHEBANG_MATCHER = new RegExp("#! */usr/bin/env +node"); + +/** + * Checks whether a file is a loadable JavaScript file. + */ +export class JavaScriptDeterminant { + private static readonly defaultPatterns = [ + "*.js", + "*.es6", + "*.jsx", + "*.mjs", + ]; + private customPatterns: ReadonlyArray = []; + + public updatePatterns(patterns: ReadonlyArray) { + this.customPatterns = patterns; + } + + public isJavaScript(aPath: string) { + const basename = path.basename(aPath); + const matchesPattern = [ + ...JavaScriptDeterminant.defaultPatterns, + ...this.customPatterns, + ].some((pattern) => match(basename, pattern, { nocase: true })); + + return matchesPattern || this.isShebang(aPath); + } + + private isShebang(aPath: string) { + try { + const buffer = Buffer.alloc(30); + const fd = fs.openSync(aPath, "r"); + fs.readSync(fd, buffer, 0, buffer.length, 0); + fs.closeSync(fd); + const line = buffer.toString(); + return NODE_SHEBANG_MATCHER.test(line); + } catch (e) { + return false; + } + } +} + +export function random(low: number, high: number): number { + return Math.floor(Math.random() * (high - low) + low); +} + +export function killTree(processId: number): void { + if (process.platform === "win32") { + const windir = process.env["WINDIR"] || "C:\\Windows"; + const TASK_KILL = path.join(windir, "System32", "taskkill.exe"); + + // when killing a process in Windows its child processes are *not* killed but become root processes. + // Therefore we use TASKKILL.EXE + try { + cp.execSync(`${TASK_KILL} /F /T /PID ${processId}`); + } catch (err) {} + } else { + // on linux and OS X we kill all direct and indirect child processes as well + try { + const cmd = path.join(__dirname, "./terminateProcess.sh"); + cp.spawnSync(cmd, [processId.toString()]); + } catch (err) {} + } +} + +export function trimLastNewline(msg: string): string { + return msg.replace(/(\n|\r\n)$/, ""); +} + +export function extendObject(toObject: T, fromObject: T): T { + for (let key in fromObject) { + if (fromObject.hasOwnProperty(key)) { + toObject[key] = fromObject[key]; + } + } + + return toObject; +} + +export function stripBOM(s: string): string { + if (s && s[0] === "\uFEFF") { + s = s.substr(1); + } + return s; +} + +const semverRegex = /v?(\d+)\.(\d+)\.(\d+)/; +export function compareSemver(a: string, b: string): number { + const aNum = versionStringToNumber(a); + const bNum = versionStringToNumber(b); + + return aNum - bNum; +} + +function versionStringToNumber(str: string): number { + const match = str.match(semverRegex); + if (!match) { + throw new Error("Invalid node version string: " + str); + } + + return ( + parseInt(match[1], 10) * 10000 + + parseInt(match[2], 10) * 100 + + parseInt(match[3], 10) + ); +} diff --git a/Source/wslSupport.ts b/Source/wslSupport.ts new file mode 100644 index 00000000..e499f856 --- /dev/null +++ b/Source/wslSupport.ts @@ -0,0 +1,134 @@ +import * as path from "path"; +import * as fs from "fs"; +import * as child_process from "child_process"; + +const isWindows = process.platform === "win32"; +const is64bit = process.arch === "x64"; + +export function subsystemForLinuxPresent(): boolean { + if (!isWindows) { + return false; + } + + const bashPath32bitApp = path.join( + process.env["SystemRoot"], + "Sysnative", + "bash.exe" + ); + const bashPath64bitApp = path.join( + process.env["SystemRoot"], + "System32", + "bash.exe" + ); + const bashPathHost = is64bit ? bashPath64bitApp : bashPath32bitApp; + return fs.existsSync(bashPathHost); +} + +function windowsPathToWSLPath(windowsPath: string): string { + if (!isWindows || !windowsPath) { + return undefined; + } else if (path.isAbsolute(windowsPath)) { + return `/mnt/${windowsPath.substr(0, 1).toLowerCase()}/${windowsPath + .substr(3) + .replace(/\\/g, "/")}`; + } else { + return windowsPath.replace(/\\/g, "/"); + } +} + +export interface ILaunchArgs { + cwd: string; + executable: string; + args: string[]; + combined: string[]; + localRoot?: string; + remoteRoot?: string; +} + +export function createLaunchArg( + useSubsytemLinux: boolean | undefined, + useExternalConsole: boolean, + cwd: string | undefined, + executable: string, + args?: string[], + program?: string +): ILaunchArgs { + if (useSubsytemLinux && subsystemForLinuxPresent()) { + const bashPath32bitApp = path.join( + process.env["SystemRoot"], + "Sysnative", + "bash.exe" + ); + const bashPath64bitApp = path.join( + process.env["SystemRoot"], + "System32", + "bash.exe" + ); + const bashPathHost = is64bit ? bashPath64bitApp : bashPath32bitApp; + const subsystemLinuxPath = useExternalConsole + ? bashPath64bitApp + : bashPathHost; + + const bashCommand = [executable] + .concat(args || []) + .map((element) => { + if (element === program) { + // workaround for issue #35249 + element = element.replace(/\\/g, "/"); + } + return element.indexOf(" ") > 0 ? `'${element}'` : element; + }) + .join(" "); + return { + cwd, + executable: subsystemLinuxPath, + args: ["-ic", bashCommand], + combined: [subsystemLinuxPath].concat(["-ic", bashCommand]), + localRoot: cwd, + remoteRoot: windowsPathToWSLPath(cwd), + }; + } else { + return { + cwd: cwd, + executable: executable, + args: args || [], + combined: [executable].concat(args || []), + }; + } +} + +export function spawn( + useWSL: boolean, + executable: string, + args?: string[], + options?: child_process.SpawnOptions +) { + const launchArgs = createLaunchArg( + useWSL, + false, + undefined, + executable, + args + ); + return child_process.spawn(launchArgs.executable, launchArgs.args, options); +} + +export function spawnSync( + useWSL: boolean, + executable: string, + args?: string[], + options?: child_process.SpawnSyncOptions +) { + const launchArgs = createLaunchArg( + useWSL, + false, + undefined, + executable, + args + ); + return child_process.spawnSync( + launchArgs.executable, + launchArgs.args, + options + ); +} diff --git a/package-lock.json b/package-lock.json index 55bdf933..82a836c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1210,50 +1210,6 @@ } } }, - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - } - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, "es5-ext": { "version": "0.10.46", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", @@ -1298,18 +1254,18 @@ "es6-symbol": "^3.1.1" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", @@ -1526,21 +1482,10 @@ "dev": true }, "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - } - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true }, "flush-write-stream": { "version": "1.1.1", @@ -2383,12 +2328,6 @@ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", "dev": true }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "gulp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.0.tgz", @@ -2954,12 +2893,6 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true - }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -2980,12 +2913,6 @@ } } }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -3082,6 +3009,12 @@ "path-is-inside": "^1.0.1" } }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -3097,15 +3030,6 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", @@ -3115,23 +3039,6 @@ "is-unc-path": "^1.0.0" } }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - } - } - }, "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -3141,6 +3048,12 @@ "unc-path-regex": "^0.1.2" } }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -3178,13 +3091,20 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + } } }, "json-stable-stringify-without-jsonify": { @@ -3454,41 +3374,62 @@ } }, "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { - "chalk": "^2.4.2" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "dependencies": { "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } } } @@ -3678,47 +3619,59 @@ } }, "mocha": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", - "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "requires": { - "ansi-colors": "3.2.3", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" }, "dependencies": { "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -3726,9 +3679,9 @@ } }, "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "braces": { @@ -3741,34 +3694,80 @@ } }, "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" } }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "fill-range": { @@ -3780,17 +3779,33 @@ "to-regex-range": "^5.0.1" } }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "optional": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -3799,6 +3814,17 @@ "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "glob-parent": { @@ -3810,6 +3836,12 @@ "is-glob": "^4.0.1" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3819,10 +3851,16 @@ "binary-extensions": "^2.0.0" } }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -3834,10 +3872,39 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "normalize-path": { @@ -3846,22 +3913,66 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { - "picomatch": "^2.0.4" + "picomatch": "^2.2.1" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" } }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "to-regex-range": { @@ -3873,14 +3984,43 @@ "is-number": "^7.0.0" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "isexe": "^2.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true } } }, @@ -3918,6 +4058,12 @@ "dev": true, "optional": true }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -3964,24 +4110,6 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, "noice-json-rpc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/noice-json-rpc/-/noice-json-rpc-1.2.0.tgz", @@ -4086,12 +4214,6 @@ } } }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, "object-keys": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", @@ -4131,16 +4253,6 @@ "isobject": "^3.0.0" } }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, "object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", @@ -4404,9 +4516,9 @@ "dev": true }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { @@ -4561,6 +4673,15 @@ "side-channel": "^1.0.4" } }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -4872,6 +4993,15 @@ "sver-compat": "^1.5.0" } }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -5199,48 +5329,6 @@ } } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -5272,9 +5360,9 @@ "dev": true }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { @@ -6166,18 +6254,6 @@ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==" }, - "vso-node-api": { - "version": "6.1.2-preview", - "resolved": "https://registry.npmjs.org/vso-node-api/-/vso-node-api-6.1.2-preview.tgz", - "integrity": "sha1-qrNUbfJFHs2JTgcbuZtd8Zxfp48=", - "dev": true, - "requires": { - "q": "^1.0.1", - "tunnel": "0.0.4", - "typed-rest-client": "^0.9.0", - "underscore": "^1.8.3" - } - }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", @@ -6193,41 +6269,11 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true }, "wrap-ansi": { "version": "2.1.0", @@ -6351,14 +6397,29 @@ } }, "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } } }, "yauzl": { @@ -6379,6 +6440,12 @@ "requires": { "buffer-crc32": "~0.2.3" } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index 831b2db7..9959c503 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "gulp-typescript": "^5.0.0", "gulp-util": "^3.0.5", "minimist": "^1.2.5", - "mocha": "^7.1.2", + "mocha": "^10.2.0", "tslint": "^5.7.0", "typescript": "^3.9.3", "vsce": "^1.95.1", diff --git a/testdata/calls-between-files-with-exception/Source/sourceA.ts b/testdata/calls-between-files-with-exception/Source/sourceA.ts new file mode 100644 index 00000000..64b4a3da --- /dev/null +++ b/testdata/calls-between-files-with-exception/Source/sourceA.ts @@ -0,0 +1,8 @@ +import { callbackCaller } from "./sourceB"; + +function f() { + console.log("mapped"); +} + +callbackCaller(f); +console.log("stepped over caught exception"); diff --git a/testdata/calls-between-files-with-exception/Source/sourceB.ts b/testdata/calls-between-files-with-exception/Source/sourceB.ts new file mode 100644 index 00000000..062e78d4 --- /dev/null +++ b/testdata/calls-between-files-with-exception/Source/sourceB.ts @@ -0,0 +1,7 @@ +export function callbackCaller(cb: Function): void { + try { + throw new Error("test error"); + } catch (e) {} + + cb(); +} diff --git a/testdata/calls-between-sourcemapped-files/Source/sourceA.ts b/testdata/calls-between-sourcemapped-files/Source/sourceA.ts new file mode 100644 index 00000000..a64fa733 --- /dev/null +++ b/testdata/calls-between-sourcemapped-files/Source/sourceA.ts @@ -0,0 +1,9 @@ +import { callbackCaller } from "./sourceB"; + +function f() { + console.log("mapped"); +} + +setInterval(() => { + callbackCaller(f); +}, 500); diff --git a/testdata/calls-between-sourcemapped-files/Source/sourceB.ts b/testdata/calls-between-sourcemapped-files/Source/sourceB.ts new file mode 100644 index 00000000..882189bb --- /dev/null +++ b/testdata/calls-between-sourcemapped-files/Source/sourceB.ts @@ -0,0 +1,3 @@ +export function callbackCaller(cb: Function): void { + cb(); +} diff --git a/testdata/sourcemap-no-sourceMappingURL/Source/classes.ts b/testdata/sourcemap-no-sourceMappingURL/Source/classes.ts new file mode 100644 index 00000000..a0d85afc --- /dev/null +++ b/testdata/sourcemap-no-sourceMappingURL/Source/classes.ts @@ -0,0 +1,21 @@ +class Foo { + private _name: string; + + constructor(name: string) { + this._name = name; + } + + getName(): string { + return this._name; + } +} + +class Bar extends Foo { + getName(): string { + return super.getName() + " Doe"; + } +} + +var bar = new Bar("John2"); + +console.log(bar.getName()); // John Doe diff --git a/testdata/sourcemaps-2574/Source/classes.ts b/testdata/sourcemaps-2574/Source/classes.ts new file mode 100644 index 00000000..a0d85afc --- /dev/null +++ b/testdata/sourcemaps-2574/Source/classes.ts @@ -0,0 +1,21 @@ +class Foo { + private _name: string; + + constructor(name: string) { + this._name = name; + } + + getName(): string { + return this._name; + } +} + +class Bar extends Foo { + getName(): string { + return super.getName() + " Doe"; + } +} + +var bar = new Bar("John2"); + +console.log(bar.getName()); // John Doe diff --git a/testdata/sourcemaps-inline/Source/classes.ts b/testdata/sourcemaps-inline/Source/classes.ts new file mode 100644 index 00000000..a0d85afc --- /dev/null +++ b/testdata/sourcemaps-inline/Source/classes.ts @@ -0,0 +1,21 @@ +class Foo { + private _name: string; + + constructor(name: string) { + this._name = name; + } + + getName(): string { + return this._name; + } +} + +class Bar extends Foo { + getName(): string { + return super.getName() + " Doe"; + } +} + +var bar = new Bar("John2"); + +console.log(bar.getName()); // John Doe diff --git a/testdata/sourcemaps-js-entrypoint/Source/classes.ts b/testdata/sourcemaps-js-entrypoint/Source/classes.ts new file mode 100644 index 00000000..a0d85afc --- /dev/null +++ b/testdata/sourcemaps-js-entrypoint/Source/classes.ts @@ -0,0 +1,21 @@ +class Foo { + private _name: string; + + constructor(name: string) { + this._name = name; + } + + getName(): string { + return this._name; + } +} + +class Bar extends Foo { + getName(): string { + return super.getName() + " Doe"; + } +} + +var bar = new Bar("John2"); + +console.log(bar.getName()); // John Doe diff --git a/testdata/sourcemaps-local-paths/Source/classes.ts b/testdata/sourcemaps-local-paths/Source/classes.ts new file mode 100644 index 00000000..175fc100 --- /dev/null +++ b/testdata/sourcemaps-local-paths/Source/classes.ts @@ -0,0 +1,21 @@ +class Foo { + private _name: string; + + constructor(name: string) { + this._name = name; + } + + getName(): string { + return this._name; + } +} + +class Bar extends Foo { + getName(): string { + return super.getName() + " Doe"; + } +} + +const bar = new Bar("John2"); + +console.log(bar.getName()); // John Doe diff --git a/testdata/sourcemaps-setinterval/Source/file2.ts b/testdata/sourcemaps-setinterval/Source/file2.ts new file mode 100644 index 00000000..6519cb35 --- /dev/null +++ b/testdata/sourcemaps-setinterval/Source/file2.ts @@ -0,0 +1,11 @@ +export class Foo { + private _name: string; + + constructor(name: string) { + this._name = name; + } + + getName(): string { + return this._name; + } +} diff --git a/testdata/sourcemaps-setinterval/Source/program.ts b/testdata/sourcemaps-setinterval/Source/program.ts new file mode 100644 index 00000000..d41d7191 --- /dev/null +++ b/testdata/sourcemaps-setinterval/Source/program.ts @@ -0,0 +1,6 @@ +console.log("Program loaded"); + +import { Foo } from "./file2"; +const foo = new Foo("foo"); + +setInterval(() => foo.getName(), 100); diff --git a/testdata/sourcemaps-with-and-without/Source/mapped.ts b/testdata/sourcemaps-with-and-without/Source/mapped.ts new file mode 100644 index 00000000..d6a81666 --- /dev/null +++ b/testdata/sourcemaps-with-and-without/Source/mapped.ts @@ -0,0 +1,7 @@ +import { callbackCaller } from "./unmapped"; + +function f() { + console.log("mapped"); +} + +callbackCaller(f); diff --git a/testdata/symlinked-file/Source/file.js b/testdata/symlinked-file/Source/file.js new file mode 100644 index 00000000..023c4e76 --- /dev/null +++ b/testdata/symlinked-file/Source/file.js @@ -0,0 +1 @@ +console.log("file.js");