diff --git a/package.json b/package.json index 9c35d6f4..e6cb3503 100644 --- a/package.json +++ b/package.json @@ -79,10 +79,6 @@ "default": false, "description": "Disable notification on long running tasks." }, - "truffle-vscode.storageAccount.name": { - "type": "string", - "scope": "Storage Account name" - }, "truffle-vscode.coreSDK": { "type": "string", "scope": "Core SDK for extensions backend", @@ -764,6 +760,8 @@ "@commitlint/cli": "^17.0.3", "@commitlint/config-conventional": "^17.0.3", "@types/big.js": "^6.1.2", + "@types/chai": "^4.3.4", + "@types/chai-as-promised": "^7.1.5", "@types/copy-webpack-plugin": "^8.0.1", "@types/download": "^6.2.4", "@types/estree": "^0.0.52", @@ -780,7 +778,7 @@ "@types/rewire": "^2.5.28", "@types/rimraf": "^3.0.2", "@types/semver": "^6.0.0", - "@types/sinon": "^7.0.11", + "@types/sinon": "^10.0.13", "@types/source-map": "^0.5.2", "@types/uuid": "^3.4.4", "@types/vscode": "1.66.0", @@ -789,6 +787,8 @@ "@vscode/debugadapter": "^1.55.1", "@vscode/debugprotocol": "^1.55.1", "@vscode/test-electron": "^2.1.3", + "chai": "^4.3.7", + "chai-as-promised": "^7.1.1", "copy-webpack-plugin": "^10.0.0", "copyfiles": "^2.4.1", "decache": "^4.5.1", @@ -797,13 +797,13 @@ "husky": "^8.0.1", "istanbul": "^0.4.5", "lint-staged": "^8.2.0", - "mocha": "^6.2.3", + "mocha": "^10.0.0", "mockery": "^2.1.0", "prettier": "2.7.1", "pretty-quick": "^3.1.3", "remap-istanbul": "^0.13.0", "rewire": "^4.0.1", - "sinon": "^7.3.2", + "sinon": "^14.0.2", "truffle": "^5.5.30", "ts-loader": "9.3.1", "ts-node": "^10.8.1", diff --git a/resources/ganache/assets/icons/contract.svg b/resources/ganache/assets/icons/contract.svg index b2912dd9..0050757d 100644 --- a/resources/ganache/assets/icons/contract.svg +++ b/resources/ganache/assets/icons/contract.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/resources/ganache/assets/icons/events.svg b/resources/ganache/assets/icons/events.svg index c55dceb4..7bc1e825 100644 --- a/resources/ganache/assets/icons/events.svg +++ b/resources/ganache/assets/icons/events.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/Constants.ts b/src/Constants.ts index 12c6fa18..186942ce 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -18,6 +18,11 @@ export enum RequiredApps { dashboard = 'dashboard', } +export enum OptionalApps { + hardhat = 'hardhat', +} +export type AppTypes = RequiredApps | OptionalApps; + export enum NotificationOptions { error = 'error', info = 'info', @@ -119,6 +124,10 @@ export class Constants { max: '', min: '5.5.0', }, + [OptionalApps.hardhat]: { + max: '', + min: '2.9.0', + }, }; public static telemetryEvents = { @@ -607,6 +616,8 @@ export class Constants { FetchingBoxesHasFailed: 'An error occurred while fetching boxes', ContractFolderNotExists: 'There is no contract directory in this workspace', UriHandlerError: 'Badly formatted. Ensure that the command and arguments are described correctly', + HHNoDefaultDeploy: + 'Hardhat has no default deploy command. Consider using the HardHat Deploy Plugin: [hardhat-deploy](https://github.com/wighawag/hardhat-deploy)', }; public static informationMessage = { @@ -678,11 +689,11 @@ export class Constants { }; public static userSettings = { coreSdkSettingsKey: 'truffle-vscode.coreSDK', - storageAccountUserSettingsKey: 'truffle-vscode.storageAccount.name', }; public static coreSdk = { truffle: 'Truffle', + hardhat: 'Hardhat', }; public static initialize(context: ExtensionContext) { diff --git a/src/Output.ts b/src/Output.ts index ab05cb81..bab4e135 100644 --- a/src/Output.ts +++ b/src/Output.ts @@ -13,6 +13,8 @@ export enum OutputLabel { requirements = 'Truffle: Requirements', telemetryClient = 'Truffle: Telemetry Client', treeManager = 'Truffle: Service Tree Manager', + sdkCoreCommands = 'Truffle: SDK Commands', + hardhatCommands = 'Truffle: Hardhat Commands', } export class Output { diff --git a/src/commands/DebuggerCommands.ts b/src/commands/DebuggerCommands.ts index 44584e3d..c08cc5b8 100644 --- a/src/commands/DebuggerCommands.ts +++ b/src/commands/DebuggerCommands.ts @@ -8,8 +8,9 @@ import {DEBUG_TYPE} from '@/debugAdapter/constants/debugAdapter'; import {DebugNetwork} from '@/debugAdapter/debugNetwork'; import {TransactionProvider} from '@/debugAdapter/transaction/transactionProvider'; import {Web3Wrapper} from '@/debugAdapter/web3Wrapper'; -import {getTruffleWorkspace, getPathByPlatform} from '@/helpers/workspace'; +import {getWorkspaceForUri} from '@/helpers/AbstractWorkspace'; import {showInputBox, showQuickPick} from '@/helpers/userInteraction'; +import {getPathByPlatform} from '@/helpers/WorkspaceHelpers'; import {Telemetry} from '@/TelemetryClient'; import {DebuggerTypes} from '@/debugAdapter/models/debuggerTypes'; @@ -19,7 +20,7 @@ export namespace DebuggerCommands { export async function startSolidityDebugger() { Telemetry.sendEvent('DebuggerCommands.startSolidityDebugger.commandStarted'); - const workspaceUri = (await getTruffleWorkspace()).workspace; + const workspaceUri = (await getWorkspaceForUri()).workspace; const workingDirectory = getPathByPlatform(workspaceUri); const debugNetwork = new DebugNetwork(workingDirectory); await debugNetwork.load(); @@ -95,7 +96,7 @@ async function getQuickPickItems(txProvider: TransactionProvider) { /** * Responsible for starting the Solidity debugger with the given arguments. - * + * @param args The `DebugArgs` to initialize the `DebugSession`. */ export async function startDebugging(args: DebuggerTypes.DebugArgs): Promise { diff --git a/src/commands/HardhatCommands.ts b/src/commands/HardhatCommands.ts new file mode 100644 index 00000000..33beeb9f --- /dev/null +++ b/src/commands/HardhatCommands.ts @@ -0,0 +1,44 @@ +// Copyright (c) 2022. Consensys Software Inc. All rights reserved. +// Licensed under the MIT license. + +import {Constants, NotificationOptions, OptionalApps} from '@/Constants'; + +import {outputCommandHelper} from '@/helpers'; +import {required} from '@/helpers/required'; +import {showIgnorableNotification, showNotification} from '@/helpers/userInteraction'; +import {AbstractWorkspace} from '@/helpers/AbstractWorkspace'; +import {Output, OutputLabel} from '@/Output'; +import {Telemetry} from '@/TelemetryClient'; +import {commands, Uri} from 'vscode'; + +export async function buildContracts(ws: AbstractWorkspace, uri?: Uri): Promise { + Telemetry.sendEvent('HardhatCommands.buildContracts.commandStarted'); + if (!(await required.checkAppsSilentForUri(ws.workspace, OptionalApps.hardhat))) { + Telemetry.sendEvent('HardhatCommands.buildContracts.hardhatInstallationMissing'); + await showNotification({ + message: 'Hardhat is not installed, please install to continue...', + type: NotificationOptions.error, + }); + // await required.installHardhat(required.Scope.locally); + return; + } + + const workspaceDir = ws.workspace.fsPath; + + Output.outputLine(OutputLabel.hardhatCommands, `compiling: ${JSON.stringify(uri)} : ${workspaceDir}`); + const args: string[] = [OptionalApps.hardhat, 'compile']; + + // hardhat will compile all contracts, not one specifically. + Output.outputLine( + OutputLabel.hardhatCommands, + `Building: ${args} DIR: ${workspaceDir} Workspace: ${JSON.stringify(ws)} ` + ); + + await showIgnorableNotification(Constants.statusBarMessages.buildingContracts, async () => { + Output.show(); + await outputCommandHelper.executeCommand(workspaceDir, 'npx', args.join(' ')); + commands.executeCommand('truffle-vscode.views.deployments.refresh'); + + Telemetry.sendEvent('HardhatCommands.buildContracts.commandFinished'); + }); +} diff --git a/src/commands/SdkCoreCommands.ts b/src/commands/SdkCoreCommands.ts index 104c0956..fda64199 100644 --- a/src/commands/SdkCoreCommands.ts +++ b/src/commands/SdkCoreCommands.ts @@ -1,20 +1,39 @@ // Copyright (c) Consensys Software Inc. All rights reserved. // Licensed under the MIT license. -import {Memento, window, Uri} from 'vscode'; -import {Constants} from '@/Constants'; -import {userSettings} from '../helpers'; -import {IExtensionAdapter, TruffleExtensionAdapter} from '@/services/extensionAdapter'; +import {getWorkspaceForUri, WorkspaceType} from '@/helpers/AbstractWorkspace'; +import {Output, OutputLabel} from '@/Output'; + +import { + HardHatExtensionAdapter, + IExtensionAdapter, + TruffleExtensionAdapter, + UnknownExtensionAdapter, +} from '@/services/extensionAdapter'; +import {Uri, window} from 'vscode'; class SdkCoreCommands { - private extensionAdapter!: IExtensionAdapter; - - public async initialize(_globalState: Memento): Promise { - const sdk = await this.getCoreSdk(); - this.extensionAdapter = this.getExtensionAdapter(sdk.userValue ? sdk.userValue : sdk.defaultValue); - this.extensionAdapter.validateExtension().catch((error) => { - window.showErrorMessage(error.message); - }); + private extensionAdapters = new Map(); + + public getExtensionAdapter(sdkVal: WorkspaceType): IExtensionAdapter | undefined { + if (this.extensionAdapters.has(sdkVal)) { + return this.extensionAdapters.get(sdkVal); + } + // let's initialise it otherwise + const adapter = this.initExtensionAdapter(sdkVal); + adapter.validateExtension().then( + (_) => { + Output.outputLine( + OutputLabel.sdkCoreCommands, + `Configuration Initialized. SdkCoreProvider: ${adapter.constructor.name}` + ); + }, + (error) => { + window.showErrorMessage(error.message); + } + ); + this.extensionAdapters.set(sdkVal, adapter); + return adapter; } /** @@ -23,7 +42,10 @@ class SdkCoreCommands { * @param contractUri if provided, it is the `Uri` of the smart contract to be compiled. */ public async build(contractUri?: Uri): Promise { - return this.extensionAdapter.build(contractUri); + const ws = await getWorkspaceForUri(contractUri); + const buildUri = contractUri ? contractUri : ws.workspace; + const adapter = this.getExtensionAdapter(ws.workspaceType); + return adapter!.build(ws, buildUri); } /** @@ -32,17 +54,20 @@ class SdkCoreCommands { * @param contractUri FIXME: Is this used? */ public async deploy(contractUri?: Uri): Promise { - return this.extensionAdapter.deploy(contractUri); + const ws = await getWorkspaceForUri(contractUri); + const deployUri = contractUri ? contractUri : ws.workspace; + const adapter = this.getExtensionAdapter(ws.workspaceType); + return adapter!.deploy(ws, deployUri); } - private async getCoreSdk() { - return userSettings.getConfigurationAsync(Constants.userSettings.coreSdkSettingsKey); - } - - private getExtensionAdapter(sdk: string): IExtensionAdapter { + private initExtensionAdapter(sdk: WorkspaceType): IExtensionAdapter { switch (sdk) { - default: + case WorkspaceType.HARDHAT: + return new HardHatExtensionAdapter(); + case WorkspaceType.TRUFFLE: return new TruffleExtensionAdapter(); + default: + return new UnknownExtensionAdapter(); } } } diff --git a/src/commands/TruffleCommands.ts b/src/commands/TruffleCommands.ts index 93a53bb2..311588d4 100644 --- a/src/commands/TruffleCommands.ts +++ b/src/commands/TruffleCommands.ts @@ -14,7 +14,7 @@ import {getTruffleWorkspace} from '@/helpers/workspace'; import {required} from '@/helpers/required'; import {showQuickPick, showConfirmPaidOperationDialog, showIgnorableNotification} from '@/helpers/userInteraction'; -import {getPathByPlatform} from '@/helpers/workspace'; +import {getPathByPlatform} from '@/helpers/WorkspaceHelpers'; import {IDeployDestination, ItemType} from '@/Models'; import {NetworkForContractItem} from '@/Models/QuickPickItems'; @@ -573,7 +573,7 @@ async function readCompiledContract(uri: Uri): Promise { return JSON.parse(data.toString()); } -function ensureFileIsContractJson(filePath: string) { +function ensureFileIsContractJson(filePath: string): void { if (path.extname(filePath) !== Constants.contract.configuration.extension.json) { const error = new Error(Constants.errorMessageStrings.InvalidContract); Telemetry.sendException(error); diff --git a/src/extension.ts b/src/extension.ts index 5e2e2b67..542b35ca 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -38,7 +38,7 @@ import {registerHelpView} from './views/HelpView'; import {OpenUrlTreeItem} from './views/lib/OpenUrlTreeItem'; import {registerGanacheDetails} from './pages/GanacheDetails'; import {registerLogView} from './views/LogView'; -import {saveTextDocument} from './helpers/workspace'; +import {saveTextDocument} from './helpers/WorkspaceHelpers'; import {StatusBarItems} from './Models/StatusBarItems/Contract'; import {UriHandlerController} from './helpers/uriHandlerController'; import {Output} from './Output'; @@ -78,7 +78,6 @@ export async function activate(context: ExtensionContext) { MnemonicRepository.initialize(context.globalState); TreeManager.initialize(context.globalState); TreeService.initialize('truffle-vscode.truffle'); - await sdkCoreCommands.initialize(context.globalState); // Starts the status bar item for automatic deploy const contractStatusBarItem = new StatusBarItems.Contract(context.globalState); @@ -231,11 +230,12 @@ export async function activate(context: ExtensionContext) { //#endregion //#region workspace subscriptions - const changeCoreSdkConfigurationListener = workspace.onDidChangeConfiguration(async (event) => { - if (event.affectsConfiguration(Constants.userSettings.coreSdkSettingsKey)) { - await sdkCoreCommands.initialize(context.globalState); - } - }); + // I think this isn't needed anymore. + // const changeCoreSdkConfigurationListener = workspace.onDidChangeConfiguration(async (event) => { + // if (event.affectsConfiguration(Constants.userSettings.coreSdkSettingsKey)) { + // await sdkCoreCommands.initialize(context.globalState); + // } + // }); const didSaveTextDocumentListener = workspace.onDidSaveTextDocument(async (event) => { // Calls the action that listens for the save files event await saveTextDocument(context.globalState, event); @@ -274,7 +274,6 @@ export async function activate(context: ExtensionContext) { signInToInfuraAccount, signOutOfInfuraAccount, showProjectsFromInfuraAccount, - changeCoreSdkConfigurationListener, didSaveTextDocumentListener, // new view - main views fileExplorerView, diff --git a/src/helpers/AbstractWorkspace.ts b/src/helpers/AbstractWorkspace.ts new file mode 100644 index 00000000..a9e94e48 --- /dev/null +++ b/src/helpers/AbstractWorkspace.ts @@ -0,0 +1,165 @@ +// Copyright (c) 2022. Consensys Software Inc. All rights reserved. +// Licensed under the MIT license. + +import {Constants} from '@/Constants'; +import {showQuickPick} from '@/helpers/userInteraction'; +import {Telemetry} from '@/TelemetryClient'; +import glob from 'glob'; +import * as path from 'path'; +import {Uri, workspace} from 'vscode'; + +/** + * The [glob](https://github.com/isaacs/node-glob#glob-primer) pattern to match Truffle/Other config file names. + */ +export const TRUFFLE_CONFIG_GLOB = 'truffle-config*.js'; +export const HARDHAT_CONFIG_GLOB = 'hardhat.config*.{js,ts}'; + +class ResolverConfig { + constructor(public type: WorkspaceType, public glob: string) {} + + async resolvePath(_uri: Uri): Promise { + return undefined; + } +} + +export enum WorkspaceType { + TRUFFLE = 'Truffle', + HARDHAT = 'Hardhat', + UNKNOWN = 'Unknown', +} + +export const WorkspaceResolvers: Array = [ + new ResolverConfig(WorkspaceType.TRUFFLE, TRUFFLE_CONFIG_GLOB), + new ResolverConfig(WorkspaceType.HARDHAT, HARDHAT_CONFIG_GLOB), +]; + +export class AbstractWorkspace { + /** + * Create an {WorkspaceType.UNKNOWN} workspace. Usually a root with no known + * framework in it. + * + * @param path - the folder root path + */ + static createUnknownWorkspace(path: Uri): AbstractWorkspace { + return new AbstractWorkspace(path, '', WorkspaceType.UNKNOWN); + } + + /** + * Create a workspace from a config path. Will try to adapt the path to get workspace root. + * @param configPath - The config file, which can drive things later on. + * @param type - The {WorkspaceType} type. This will generally not be an {WorkspaceType.UNKNOWN} + * @throws Error when you attempt to use {WorkspaceType.UNKNOWN} as the type. + */ + static createWorkspaceFromConfigPath(configPath: string, type: WorkspaceType): AbstractWorkspace { + if (type === WorkspaceType.UNKNOWN) throw new Error('Cannot use UNKNOWN workspace type with this constructor.'); + const workspacePath = Uri.parse(path.dirname(configPath)); + return new AbstractWorkspace(workspacePath, configPath, type); + } + + /** + * Constructor to derive the workspace based on either the dirName (which is the root) or the config + * file location (from the glob). This also allows us to make Unknown or generic workspace types + * to help with config/reconciling commands etc. + * + * @param workspaceUri - the directory this workspace is in + * @param configPath - the path (optionally) to the config + * @param workspaceType - the type of workspace we are dealing with. + * @private - use the static constructors above. + */ + private constructor(workspaceUri: Uri, configPath: string, public readonly workspaceType: WorkspaceType) { + //this.dirName = path.dirname(dirName).split(path.sep).pop()!.toString(); + // or path.basename(path.dirname(filename)) + this.workspace = workspaceUri; + this.dirName = configPath !== '' ? path.basename(path.dirname(configPath)) : path.basename(workspaceUri.path); + this.configName = path.basename(configPath); + this.configPath = Uri.parse(configPath); + } + + /** + * Represents the `basename`, _i.e._, the file name portion + */ + readonly configName: string; + + /** + * The last directory name where this config file is located. + */ + readonly dirName: string; + + /** + * The `Uri` root of the workspace or where hte config resides. Usually the same. + */ + readonly workspace: Uri; + + /** + * The full `Uri` path where this config file is located. + */ + readonly configPath: Uri; +} + +/** + * Using all the resolvers, resolve the projects/config files present in the workspaces. + */ +export function resolveAllWorkspaces(includeUnknown = true): AbstractWorkspace[] { + if (workspace.workspaceFolders === undefined) { + return []; + } + return workspace.workspaceFolders.flatMap((ws) => { + const foundWs = findWorkspaces(ws.uri.fsPath); + // patch in the unknown ones. + if (includeUnknown && foundWs?.length === 0) { + foundWs.push(AbstractWorkspace.createUnknownWorkspace(ws.uri)); + } + return foundWs; + }); +} + +export const findWorkspaces = (workspaceRootPath: string): AbstractWorkspace[] => { + return WorkspaceResolvers.flatMap((r) => + glob + .sync(`${workspaceRootPath}/**/${r.glob}`, { + ignore: Constants.workspaceIgnoredFolders, + }) + .map((f) => AbstractWorkspace.createWorkspaceFromConfigPath(f, r.type)) + ); +}; + +/** + * Shows the list of `workspaces` in a quick pick so the user can select + * the correct config file to use. + * + * @param workspaces list of workspace folders to display to the user. + * @returns the config file of the selected Workspace. + */ +export async function selectConfigFromQuickPick(workspaces: AbstractWorkspace[]): Promise { + const folders = workspaces.map((element) => { + return { + label: element.dirName, + description: `Type: ${element.workspaceType} : ${element.configName}`, + detail: process.platform === 'win32' ? element.dirName : element.workspace.fsPath, + workspace: element, + }; + }); + + const result = await showQuickPick(folders, { + ignoreFocusOut: true, + placeHolder: `Select a config file to use`, + }); + return result.workspace; +} + +export async function getWorkspaceForUri(contractUri?: Uri): Promise { + const workspaces = contractUri + ? findWorkspaces(workspace.getWorkspaceFolder(contractUri)!.uri.fsPath) + : resolveAllWorkspaces(); + if (workspaces.length === 0) { + const error = new Error(Constants.errorMessageStrings.VariableShouldBeDefined('Workspace root')); + Telemetry.sendException(error); + throw error; + } + + if (workspaces.length === 1) { + return workspaces[0]; + } + + return await selectConfigFromQuickPick(workspaces); +} diff --git a/src/helpers/TruffleConfiguration.ts b/src/helpers/TruffleConfiguration.ts index fe84ffb3..9fd87e1d 100644 --- a/src/helpers/TruffleConfiguration.ts +++ b/src/helpers/TruffleConfiguration.ts @@ -13,7 +13,7 @@ import path from 'path'; import {Uri} from 'vscode'; import {ICommandResult, tryExecuteCommandInFork} from './command'; import {IConfiguration, INetwork, INetworkOption, IProvider, notAllowedSymbols} from './ConfigurationReader'; -import {getPathByPlatform, getWorkspaceRoot} from './workspace'; +import {getPathByPlatform, getWorkspaceRoot} from './WorkspaceHelpers'; export class EvalTruffleConfigError extends Error { constructor(message: string, readonly reason: string) { diff --git a/src/helpers/WorkspaceHelpers.ts b/src/helpers/WorkspaceHelpers.ts new file mode 100644 index 00000000..f9aac1f2 --- /dev/null +++ b/src/helpers/WorkspaceHelpers.ts @@ -0,0 +1,64 @@ +// Copyright (c) 2022. Consensys Software Inc. All rights reserved. +// Licensed under the MIT license. + +import {TruffleCommands} from '@/commands'; +import {Constants} from '@/Constants'; +import {Telemetry} from '@/TelemetryClient'; +import * as path from 'path'; +import {Memento, TextDocument, Uri, workspace, WorkspaceFolder} from 'vscode'; + +/** + * ! We need to remove this because it does not support multiple Truffle config files. + * @param ignoreException + * @returns + */ +export function getWorkspaceRoot(ignoreException = false): string | undefined { + const workspaceRoot = + workspace.workspaceFolders && + (workspace.workspaceFolders.length === 0 ? undefined : workspace.workspaceFolders[0].uri.fsPath); + + if (workspaceRoot === undefined && !ignoreException) { + const error = new Error(Constants.errorMessageStrings.VariableShouldBeDefined('Workspace root')); + Telemetry.sendException(error); + throw error; + } + + return workspaceRoot; +} + +/** + * Method to map filepaths properly on windows machines. + * @param workspace + */ +export const getPathByPlatform = (workspace: Uri): string => + process.platform === 'win32' ? `${workspace.scheme}:${workspace.path}` : workspace.fsPath; + +/** + * Every time the `workspace.onDidSaveTextDocument` listener emits a notification, + * this function receives, identifies the file extension and calls the corresponding function. + * + * @param globalState A memento object that stores state independent of the current opened workspace. + * @param document Represents a text document, such as a source file. + */ +export async function saveTextDocument(globalState: Memento, document: TextDocument): Promise { + switch (path.extname(document.fileName)) { + case '.sol': { + // Gets the current state of the status bar item + const isAutoDeployOnSaveEnabled = globalState.get(Constants.globalStateKeys.contractAutoDeployOnSave); + + // If enabled, calls the function that performs the deployment + if (isAutoDeployOnSaveEnabled) { + await TruffleCommands.deployContracts(Uri.parse(document.fileName)); + } + break; + } + default: + break; + } +} + +/** + * Gets the first Workspace folder or undefined. + */ +export const getWorkspaceFolder = (): WorkspaceFolder | undefined => + workspace.workspaceFolders?.filter((folder) => folder.uri.scheme === 'file')[0]; diff --git a/src/helpers/command.ts b/src/helpers/command.ts index 97e6e1c4..228ac62f 100644 --- a/src/helpers/command.ts +++ b/src/helpers/command.ts @@ -31,7 +31,7 @@ export interface ICommandExecute { export async function executeCommand( workingDirectory: string | undefined, - commands: string, + command: string, ...args: string[] ): Promise { Output.outputLine( @@ -39,47 +39,46 @@ export async function executeCommand( '\n' + `Working dir: ${workingDirectory}\n` + `${Constants.executeCommandMessage.runningCommand}\n` + - `${[commands, ...args].join(' ')}` + `${[command, ...args].join(' ')}` ); Telemetry.sendEvent('command.executeCommand.tryExecuteCommandWasStarted'); - const result: ICommandResult = await tryExecuteCommand(workingDirectory, commands, ...args); + const result: ICommandResult = await tryExecuteCommand(workingDirectory, command, ...args); Output.outputLine(OutputLabel.executeCommand, Constants.executeCommandMessage.finishRunningCommand); if (result.code !== 0) { Telemetry.sendException(new Error('commands.executeCommand.resultWithIncorrectCode')); - throw new Error(Constants.executeCommandMessage.failedToRunCommand(commands.concat(' ', ...args.join(' ')))); + throw new Error(Constants.executeCommandMessage.failedToRunCommand(command.concat(' ', ...args.join(' ')))); } return result.cmdOutput; } -export function spawnProcess(workingDirectory: string | undefined, commands: string, args: string[]): ChildProcess { +export function spawnProcess(workingDirectory: string | undefined, command: string, args: string[]): ChildProcess { const options: SpawnOptions = {cwd: workingDirectory || tmpdir(), shell: true}; - return spawn(commands, args, options); + return spawn(command, args, options); } export async function tryExecuteCommand( workingDirectory: string | undefined, - commands: string, + command: string, ...args: string[] ): Promise { - const {result} = await tryExecuteCommandAsync(workingDirectory, true, commands, ...args); - + const {result} = await tryExecuteCommandAsync(workingDirectory, true, command, ...args); return result; } export async function tryExecuteCommandAsync( workingDirectory: string | undefined, writeToOutputChannel: boolean, - commands: string, + command: string, ...args: string[] ): Promise { let cmdOutput = ''; let cmdOutputIncludingStderr = ''; - const childProcess = spawnProcess(workingDirectory, commands, args); + const childProcess = spawnProcess(workingDirectory, command, args); const result = new Promise((resolve: (res: any) => void, reject: (error: Error) => void): void => { childProcess.stdout!.on('data', (data: string | Buffer) => { data = data.toString(); diff --git a/src/helpers/required.ts b/src/helpers/required.ts index fc3495aa..3d1ee035 100644 --- a/src/helpers/required.ts +++ b/src/helpers/required.ts @@ -5,9 +5,9 @@ import {getTruffleConfigUri, TruffleConfig} from '@/helpers/TruffleConfiguration import fs from 'fs-extra'; import path from 'path'; import semver from 'semver'; -import {commands, ProgressLocation, window} from 'vscode'; -import {Constants, RequiredApps} from '@/Constants'; -import {getWorkspaceRoot} from '@/helpers/workspace'; +import {commands, ProgressLocation, Uri, window} from 'vscode'; +import {Constants, RequiredApps, OptionalApps, AppTypes} from '@/Constants'; +import {getPathByPlatform, getWorkspaceFolder, getWorkspaceRoot} from '@/helpers/WorkspaceHelpers'; import {Output, OutputLabel} from '@/Output'; import {Telemetry} from '@/TelemetryClient'; import {executeCommand, tryExecuteCommand} from './command'; @@ -25,6 +25,8 @@ export namespace required { global = 0, } + type VersionCallback = () => Promise; + const currentState: {[key: string]: IRequiredVersion} = {}; const requiredApps = [RequiredApps.node, RequiredApps.npm, RequiredApps.git]; @@ -83,10 +85,15 @@ export namespace required { return valid; } - export async function checkAppsSilent(...apps: RequiredApps[]): Promise { - const versions = await getExactlyVersions(...apps); + export async function checkAppsSilent(...apps: AppTypes[]): Promise { + const rootWorkspace = getWorkspaceFolder(); + return checkAppsSilentForUri(rootWorkspace!.uri, ...apps); + } + + export async function checkAppsSilentForUri(workspaceUri: Uri, ...apps: AppTypes[]): Promise { + const versions = await getExactlyVersions(workspaceUri, ...apps); const invalid = versions - .filter((version) => apps.includes(version.app as RequiredApps)) + .filter((version) => apps.includes(version.app as AppTypes)) .some((version) => !version.isValid); Output.outputLine( @@ -152,10 +159,11 @@ export namespace required { } export async function getAllVersions(): Promise { - return getExactlyVersions(...requiredApps, ...auxiliaryApps); + const rootWorkspace = getWorkspaceFolder(); + return getExactlyVersions(rootWorkspace!.uri, ...requiredApps, ...auxiliaryApps); } - export async function getExactlyVersions(...apps: RequiredApps[]): Promise { + export async function getExactlyVersions(workspaceUri: Uri, ...apps: AppTypes[]): Promise { Output.outputLine(OutputLabel.requirements, `Get version for required apps: ${apps.join(',')}`); if (apps.includes(RequiredApps.node)) { @@ -175,10 +183,31 @@ export namespace required { currentState.ganache = currentState.ganache || (await createRequiredVersion(RequiredApps.ganache, getGanacheVersion)); } + if (apps.includes(OptionalApps.hardhat)) { + currentState.hardhat = + currentState.hardhat || + (await createRequiredVersion(OptionalApps.hardhat, getNpmPackageVersion(workspaceUri, OptionalApps.hardhat))); + } return Object.values(currentState); } + /** + * Run an NPM ls command to see if a specific package is installed within the NPM system in this project. + * @param workspaceUri - the uri of the workspace we want the npm package call to be run in. + * @param packageName - the npm specific name + */ + const getNpmPackageVersion: (workspaceUri: Uri, packageName: string) => VersionCallback = + (workspaceUri: Uri, packageName: string) => async () => { + const platformPath = getPathByPlatform(workspaceUri); + return await getVersionWithArgs( + platformPath, + RequiredApps.npm, + ['ls', '--pareseable', '--long', '--depth=0', packageName], + new RegExp(`${packageName}@(\\d+.\\d+.\\d+)`) + ); + }; + export async function getNodeVersion(): Promise { return await getVersion(RequiredApps.node, '--version', /v(\d+.\d+.\d+)/); } @@ -275,7 +304,7 @@ export namespace required { return false; } - async function createRequiredVersion(appName: string, versionFunc: () => Promise): Promise { + async function createRequiredVersion(appName: string, versionFunc: VersionCallback): Promise { const version = await versionFunc(); const requiredVersion = Constants.requiredVersions[appName]; const minRequiredVersion = typeof requiredVersion === 'string' ? requiredVersion : requiredVersion.min; @@ -318,13 +347,21 @@ export namespace required { } async function getVersion(program: string, command: string, matcher: RegExp): Promise { + return getVersionWithArgs(undefined, program, [command], matcher); + } + + async function getVersionWithArgs( + workingDirectory: string | undefined, + program: string, + commands: string[], + matcher: RegExp + ): Promise { try { - const result = await tryExecuteCommand(undefined, program, command); + const result = await tryExecuteCommand(workingDirectory, program, ...commands); if (result.code === 0) { const output = result.cmdOutput || result.cmdOutputIncludingStderr; const installedVersion = output.match(matcher); const version = semver.clean(installedVersion ? installedVersion[1] : ''); - return version || ''; } } catch (error) { diff --git a/src/helpers/workspace.ts b/src/helpers/workspace.ts index 04ddfa2d..286a7d10 100644 --- a/src/helpers/workspace.ts +++ b/src/helpers/workspace.ts @@ -1,18 +1,17 @@ // Copyright (c) Consensys Software Inc. All rights reserved. // Licensed under the MIT license. -import {Memento, TextDocument, Uri, workspace} from 'vscode'; import {Constants} from '@/Constants'; +import {showQuickPick} from '@/helpers/userInteraction'; import {Telemetry} from '@/TelemetryClient'; -import * as path from 'path'; import glob from 'glob'; -import {showQuickPick} from '@/helpers/userInteraction'; -import {TruffleCommands} from '@/commands'; +import * as path from 'path'; +import {Uri, workspace} from 'vscode'; /** * The [glob](https://github.com/isaacs/node-glob#glob-primer) pattern to match Truffle config file names. */ -const TRUFFLE_CONFIG_GLOB = 'truffle-config{,.*}.js'; +const TRUFFLE_CONFIG_GLOB = 'truffle-config*.js'; /** * A Truffle workspace is defined by the presence of a Truffle config file. @@ -127,20 +126,11 @@ export function getPathByPlatform(workspace: Uri): string { * * @returns all Truffle config files found wrapped in {@link TruffleWorkspace}. */ -export async function getAllTruffleWorkspaces(): Promise { +export function getAllTruffleWorkspaces(): TruffleWorkspace[] { if (workspace.workspaceFolders === undefined) { return []; } - - const workspaces: TruffleWorkspace[] = []; - - await Promise.all( - workspace.workspaceFolders.map(async (ws) => { - workspaces.push(...(await findTruffleWorkspaces(ws.uri.fsPath))); - }) - ); - - return workspaces; + return workspace.workspaceFolders.flatMap((ws) => findTruffleWorkspaces(ws.uri.fsPath)); } /** @@ -154,11 +144,10 @@ export async function getAllTruffleWorkspaces(): Promise { * @param workspaceRootPath the root path where to look for Truffle config files. * @returns all Truffle config files found wrapped in `TruffleWorkspace`. */ -async function findTruffleWorkspaces(workspaceRootPath: string): Promise { +function findTruffleWorkspaces(workspaceRootPath: string): TruffleWorkspace[] { const files = glob.sync(`${workspaceRootPath}/**/${TRUFFLE_CONFIG_GLOB}`, { ignore: Constants.workspaceIgnoredFolders, }); - return files.map((file) => new TruffleWorkspace(file)); } @@ -186,25 +175,3 @@ async function selectTruffleConfigFromQuickPick(workspaces: TruffleWorkspace[]): return result.truffleWorkspace; } - -/** - * Every time the `workspace.onDidSaveTextDocument` listener emits a notification, - * this function receives, identifies the file extension and calls the corresponding function. - * - * @param globalState A memento object that stores state independent of the current opened workspace. - * @param document Represents a text document, such as a source file. - */ -export async function saveTextDocument(globalState: Memento, document: TextDocument): Promise { - switch (path.extname(document.fileName)) { - case '.sol': { - // Gets the current state of the status bar item - const isAutoDeployOnSaveEnabled = globalState.get(Constants.globalStateKeys.contractAutoDeployOnSave); - - // If enabled, calls the function that performs the deployment - if (isAutoDeployOnSaveEnabled) await TruffleCommands.deployContracts(Uri.parse(document.fileName)); - break; - } - default: - break; - } -} diff --git a/src/services/contract/ContractService.ts b/src/services/contract/ContractService.ts index 849b69bb..78e7d1fd 100644 --- a/src/services/contract/ContractService.ts +++ b/src/services/contract/ContractService.ts @@ -100,4 +100,19 @@ export namespace ContractService { return path.join(workDir, dir); } + + // async function getPathDirectoryAW(directory: PathDirectoryKey, workspace?: AbstractWorkspace): Promise { + // const [workDir, name] = workspace + // ? [getPathByPlatform(workspace.workspace), workspace.configName] + // : [getWorkspaceRoot()!, undefined]; + // const configuration = await getTruffleConfiguration(workDir, name); + // + // const dir = (configuration as any)[directory]; + // + // if (dir && path.isAbsolute(dir)) { + // return dir; + // } + // + // return path.join(workDir, dir); + // } } diff --git a/src/services/extensionAdapter/HardHatExtensionAdapter.ts b/src/services/extensionAdapter/HardHatExtensionAdapter.ts new file mode 100644 index 00000000..deddc825 --- /dev/null +++ b/src/services/extensionAdapter/HardHatExtensionAdapter.ts @@ -0,0 +1,29 @@ +// Copyright (c) 2022. Consensys Software Inc. All rights reserved. +// Licensed under the MIT license. + +import {buildContracts} from '@/commands/HardhatCommands'; +import {NotificationOptions} from '@/Constants'; +import {AbstractWorkspace, WorkspaceType} from '@/helpers/AbstractWorkspace'; +import {showNotification} from '@/helpers/userInteraction'; +import {IExtensionAdapter} from '@/services/extensionAdapter/IExtensionAdapter'; +import {Constants} from '@/constants'; +import {Uri} from 'vscode'; + +export class HardHatExtensionAdapter implements IExtensionAdapter { + build(workspace: AbstractWorkspace, contractUri?: Uri): Promise { + return buildContracts(workspace, contractUri); + } + + async deploy(_: AbstractWorkspace, _uri?: Uri): Promise { + await showNotification({ + message: Constants.errorMessageStrings.HHNoDefaultDeploy, + type: NotificationOptions.error, + }); + } + + async validateExtension(): Promise { + return Promise.resolve(undefined); + } + + extensionType: WorkspaceType = WorkspaceType.HARDHAT; +} diff --git a/src/services/extensionAdapter/IExtensionAdapter.ts b/src/services/extensionAdapter/IExtensionAdapter.ts index 370fbb1e..8da10b14 100644 --- a/src/services/extensionAdapter/IExtensionAdapter.ts +++ b/src/services/extensionAdapter/IExtensionAdapter.ts @@ -1,10 +1,13 @@ // Copyright (c) Consensys Software Inc. All rights reserved. // Licensed under the MIT license. + +import {AbstractWorkspace, WorkspaceType} from '@/helpers/AbstractWorkspace'; import {Uri} from 'vscode'; export interface IExtensionAdapter { + extensionType: WorkspaceType; validateExtension: () => Promise; - build: (contractUri?: Uri) => Promise; - deploy: (contractUri?: Uri) => Promise; + build: (workspace: AbstractWorkspace, contractUri?: Uri) => Promise; + deploy: (workspace: AbstractWorkspace, contractUri?: Uri) => Promise; } diff --git a/src/services/extensionAdapter/TruffleExtensionAdapter.ts b/src/services/extensionAdapter/TruffleExtensionAdapter.ts index 3a3d6791..0af13deb 100644 --- a/src/services/extensionAdapter/TruffleExtensionAdapter.ts +++ b/src/services/extensionAdapter/TruffleExtensionAdapter.ts @@ -1,8 +1,9 @@ // Copyright (c) Consensys Software Inc. All rights reserved. // Licensed under the MIT license. +import {TruffleCommands} from '@/commands'; +import {AbstractWorkspace, WorkspaceType} from '@/helpers/AbstractWorkspace'; import {Uri} from 'vscode'; -import {TruffleCommands} from '../../commands/TruffleCommands'; import {IExtensionAdapter} from './IExtensionAdapter'; export class TruffleExtensionAdapter implements IExtensionAdapter { @@ -10,11 +11,13 @@ export class TruffleExtensionAdapter implements IExtensionAdapter { // throw new Error("Method not implemented."); }; - public build = async (uri?: Uri): Promise => { - return TruffleCommands.buildContracts(uri); + public build = async (_: AbstractWorkspace, contractUri?: Uri): Promise => { + return TruffleCommands.buildContracts(contractUri); }; - public deploy = async (uri?: Uri): Promise => { - return TruffleCommands.deployContracts(uri); + public deploy = async (_: AbstractWorkspace): Promise => { + return TruffleCommands.deployContracts(); }; + + extensionType: WorkspaceType = WorkspaceType.TRUFFLE; } diff --git a/src/services/extensionAdapter/UnknownExtensionAdapter.ts b/src/services/extensionAdapter/UnknownExtensionAdapter.ts new file mode 100644 index 00000000..2ca751d9 --- /dev/null +++ b/src/services/extensionAdapter/UnknownExtensionAdapter.ts @@ -0,0 +1,22 @@ +// Copyright (c) 2022. Consensys Software Inc. All rights reserved. +// Licensed under the MIT license. + +import {AbstractWorkspace, WorkspaceType} from '@/helpers/AbstractWorkspace'; +import {Uri} from 'vscode'; +import {IExtensionAdapter} from './IExtensionAdapter'; + +export class UnknownExtensionAdapter implements IExtensionAdapter { + public validateExtension = async (): Promise => { + // throw new Error("Method not implemented."); + }; + + public build = async (_: AbstractWorkspace, __?: Uri): Promise => { + // TODO:throw some info here? + }; + + public deploy = async (_: AbstractWorkspace): Promise => { + // TODO:throw some info here? + }; + + extensionType: WorkspaceType = WorkspaceType.UNKNOWN; +} diff --git a/src/services/extensionAdapter/index.ts b/src/services/extensionAdapter/index.ts index 8cccad5a..ec64d635 100644 --- a/src/services/extensionAdapter/index.ts +++ b/src/services/extensionAdapter/index.ts @@ -3,3 +3,5 @@ export * from './IExtensionAdapter'; export * from './TruffleExtensionAdapter'; +export * from './UnknownExtensionAdapter'; +export * from './HardHatExtensionAdapter'; diff --git a/src/views/DeploymentsView.ts b/src/views/DeploymentsView.ts index 9401d3a1..32ab1398 100644 --- a/src/views/DeploymentsView.ts +++ b/src/views/DeploymentsView.ts @@ -14,7 +14,7 @@ import { Command, ThemeColor, } from 'vscode'; -import {getChain, getExplorerLink} from '../functions/explorer'; +import {getChain, getExplorerLink} from '@/functions/explorer'; import {OpenUrlTreeItem} from './lib/OpenUrlTreeItem'; import {ContractService} from '@/services/contract/ContractService'; import {getAllTruffleWorkspaces, TruffleWorkspace} from '@/helpers/workspace'; @@ -300,7 +300,10 @@ class DeploymentsView implements TreeDataProvider { return (element as TreeParentItem).loadChildren(); } - const truffleWorkspaces = await getAllTruffleWorkspaces(); + // TODO: just the truffle ones maam. + // const workspaces = resolveAllWorkspaces().filter((ws) => ws.workspaceType === WorkspaceType.TRUFFLE); + + const truffleWorkspaces = getAllTruffleWorkspaces(); if (truffleWorkspaces.length === 0) { return []; } else if (truffleWorkspaces.length === 1) { diff --git a/src/views/FileExplorer.ts b/src/views/FileExplorer.ts index a9838222..9656a193 100644 --- a/src/views/FileExplorer.ts +++ b/src/views/FileExplorer.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import * as rimraf from 'rimraf'; import * as vscode from 'vscode'; import {ThemeColor, ThemeIcon, Uri} from 'vscode'; -import {Constants} from '../Constants'; +import {Constants} from '@/Constants'; import {ContractService} from '@/services/contract/ContractService'; //#region Utilities @@ -188,6 +188,26 @@ export type Entry = vscode.Uri & { contextValue?: string; }; +// /** +// * Represents a top-level `TreeItem` for our file view... +// * +// * This gives us a few more free items in terms of customisation over the original view item. +// */ +// class TreeItemEntry extends TreeItem { +// constructor( +// path: string, +// uri: vscode.Uri, +// public readonly type: FileType, +// description?: string, +// icon?: vscode.ThemeIcon +// ) { +// super(path); +// this.resourceUri = uri; +// if (icon) this.iconPath = icon; +// if (description) this.description = description; +// } +// } + export type TElementTypes = { contextValue: string; type: vscode.FileType; @@ -256,9 +276,7 @@ export class FileSystemProvider implements vscode.TreeDataProvider, vscod {recursive: options.recursive}, async (event: string, filename: string | Buffer) => { const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString())); - // TODO support excludes (using minimatch library?) - this._onDidChangeFile.fire([ { type: @@ -364,7 +382,6 @@ export class FileSystemProvider implements vscode.TreeDataProvider, vscod if (!parentExists) { await _.mkdir(path.dirname(newUri.fsPath)); } - return _.rename(oldUri.fsPath, newUri.fsPath); } @@ -389,7 +406,8 @@ export class FileSystemProvider implements vscode.TreeDataProvider, vscod const elements: Entry[] = []; // Gets the truffle workspaces - const workspaces = await getAllTruffleWorkspaces(); + const workspaces = getAllTruffleWorkspaces(); + console.log(`getChildren: `, {workspaces, element}); // Checks if there are any truffle workspaces if (workspaces.length === 0) { @@ -420,7 +438,7 @@ export class FileSystemProvider implements vscode.TreeDataProvider, vscod type: vscode.FileType.Directory, label: path.basename(contractFolder), iconPath: new ThemeIcon('file-directory'), - description: path.basename(path.dirname(contractFolder)), + description: `${path.basename(path.dirname(contractFolder))} - (${workspace.truffleConfigName})`, contextValue: this.getTreeItemContextValue(vscode.FileType.Directory, true), }) ); @@ -475,7 +493,7 @@ export class FileSystemProvider implements vscode.TreeDataProvider, vscod } /** - * Gets the context value from element according on the type: root, folder, or file. + * Gets the context value from element according to the type: root, folder, or file. * The `context Value` offers a filter on the file explorer menu that filters action alternatives such as: * `Create Contract`, `Build Contracts`, `Build This Contract`, `Deploy Contracts` and `Debug Transaction`. * diff --git a/test/TruffleConfig.test.ts b/test/TruffleConfig.test.ts index 9d946aef..5d9e64ca 100644 --- a/test/TruffleConfig.test.ts +++ b/test/TruffleConfig.test.ts @@ -7,7 +7,7 @@ import fs from 'fs-extra'; import path from 'path'; import sinon from 'sinon'; import {Constants} from '@/Constants'; -import * as helpers from '@/helpers/workspace'; +import * as helpers from '@/helpers/WorkspaceHelpers'; import * as commands from '../src/helpers/command'; import {ICommandResult} from '@/helpers/command'; import {getTruffleConfiguration, getTruffleConfigUri, TruffleConfig} from '@/helpers/TruffleConfiguration'; diff --git a/test/TruffleExtensionAdapter.test.ts b/test/TruffleExtensionAdapter.test.ts index 8c7f0975..e141cd45 100644 --- a/test/TruffleExtensionAdapter.test.ts +++ b/test/TruffleExtensionAdapter.test.ts @@ -1,21 +1,24 @@ // Copyright (c) Consensys Software Inc. All rights reserved. // Licensed under the MIT license. +import * as AW from '@/helpers/AbstractWorkspace'; import assert from 'assert'; -import sinon from 'sinon'; -import {TruffleCommands} from '../src/commands/TruffleCommands'; -import {TruffleExtensionAdapter} from '../src/services/extensionAdapter'; +import sinon, {mock} from 'sinon'; +import {TruffleCommands} from '@/commands'; +import {TruffleExtensionAdapter} from '@/services/extensionAdapter'; describe('TruffleExtensionAdapter', () => { let buildContractsMock: sinon.SinonStub; let deployContractsMock: sinon.SinonStub; let truffleExtensionAdapter: TruffleExtensionAdapter; + let workspaceMock: any; beforeEach(() => { buildContractsMock = sinon.stub(TruffleCommands, 'buildContracts'); deployContractsMock = sinon.stub(TruffleCommands, 'deployContracts'); truffleExtensionAdapter = new TruffleExtensionAdapter(); + workspaceMock = mock(AW.AbstractWorkspace); }); afterEach(() => { @@ -24,15 +27,15 @@ describe('TruffleExtensionAdapter', () => { it('build method should call truffleCommands.buildContracts', async () => { // Act - await truffleExtensionAdapter.build(); + await truffleExtensionAdapter.build(workspaceMock); // Assert assert.strictEqual(buildContractsMock.calledOnce, true, 'TruffleCommands.buildContracts should be called once'); }); - it('deploy method should call truffleCommands.buildContracts', async () => { + it('deploy method should call truffleCommands.deployContracts', async () => { // Act - await truffleExtensionAdapter.deploy(); + await truffleExtensionAdapter.deploy(workspaceMock); // Assert assert.strictEqual(deployContractsMock.calledOnce, true, 'TruffleCommands.deployContracts should be called once'); diff --git a/test/commands/DebuggerCommands.test.ts b/test/commands/DebuggerCommands.test.ts index e12d82d8..df2c9ba6 100644 --- a/test/commands/DebuggerCommands.test.ts +++ b/test/commands/DebuggerCommands.test.ts @@ -1,30 +1,32 @@ // Copyright (c) Consensys Software Inc. All rights reserved. // Licensed under the MIT license. +import {DebugNetwork} from '@/debugAdapter/debugNetwork'; +import {ITransactionResponse} from '@/debugAdapter/models/ITransactionResponse'; +import {TransactionProvider} from '@/debugAdapter/transaction/transactionProvider'; +import {AbstractWorkspace, WorkspaceType} from '@/helpers/AbstractWorkspace'; + +import * as aw from '@/helpers/AbstractWorkspace'; import assert from 'assert'; import path from 'path'; import sinon from 'sinon'; import {debug, QuickPickItem, Uri, workspace} from 'vscode'; -import {DebugNetwork} from '@/debugAdapter/debugNetwork'; -import {ITransactionResponse} from '@/debugAdapter/models/ITransactionResponse'; -import {TransactionProvider} from '@/debugAdapter/transaction/transactionProvider'; - import * as userInteraction from '../../src/helpers/userInteraction'; import {TestConstants} from '../TestConstants'; -import * as helpers from '@/helpers/workspace'; import {shortenHash} from '@/commands/DebuggerCommands'; -const truffleWorkspace = new helpers.TruffleWorkspace( - path.join(__dirname, TestConstants.truffleCommandTestDataFolder, 'truffle-config.js') +const truffleWorkspace = AbstractWorkspace.createWorkspaceFromConfigPath( + path.join(__dirname, TestConstants.truffleCommandTestDataFolder, 'truffle-config.js'), + WorkspaceType.TRUFFLE ); describe('DebuggerCommands mock tests', () => { let mockGetTxHashes: sinon.SinonStub<[(number | undefined)?], Promise>; let mockGetTxInfos: sinon.SinonStub<[string[]], Promise>; let debugCommands: any; - let getWorkspacesMock: sinon.SinonStub<[contractUri?: Uri], Promise>; + let getWorkspacesMock: sinon.SinonStub<[contractUri?: Uri], Promise>; beforeEach(() => { mockGetTxHashes = sinon.stub(TransactionProvider.prototype, 'getLastTransactionHashes'); @@ -32,7 +34,7 @@ describe('DebuggerCommands mock tests', () => { mockGetTxInfos = sinon.stub(TransactionProvider.prototype, 'getTransactionsInfo'); mockGetTxInfos.resolves([]); - getWorkspacesMock = sinon.stub(helpers, 'getTruffleWorkspace'); + getWorkspacesMock = sinon.stub(aw, 'getWorkspaceForUri'); getWorkspacesMock.returns(Promise.resolve(truffleWorkspace)); sinon.stub(debug, 'startDebugging').resolves(); diff --git a/test/commands/GanacheCommands.int.test.ts b/test/commands/GanacheCommands.int.test.ts index 5c7630bf..38832fd4 100644 --- a/test/commands/GanacheCommands.int.test.ts +++ b/test/commands/GanacheCommands.int.test.ts @@ -1,18 +1,18 @@ // Copyright (c) Consensys Software Inc. All rights reserved. // Licensed under the MIT license. +import {GanacheCommands} from '@/commands'; +import {IExtensionItem, LocalProject, LocalService, Service, TLocalProjectOptions} from '@/Models/TreeItems'; +import {TreeManager} from '@/services'; +import {ProjectView} from '@/ViewItems'; import assert from 'assert'; import cp, {ChildProcess} from 'child_process'; import rp from 'request-promise'; import sinon from 'sinon'; import stream from 'stream'; import * as vscode from 'vscode'; -import {GanacheCommands} from '../../src/commands'; import * as commands from '../../src/helpers/command'; import * as shell from '../../src/helpers/shell'; -import {IExtensionItem, LocalProject, LocalService, Service, TLocalProjectOptions} from '../../src/Models/TreeItems'; -import {TreeManager} from '../../src/services'; -import {ProjectView} from '../../src/ViewItems'; describe('Integration tests GanacheCommands', () => { const defaultPort = 8545; @@ -20,6 +20,7 @@ describe('Integration tests GanacheCommands', () => { let serviceItems: Service[]; let loadStateMock: sinon.SinonStub<[], IExtensionItem[]>; let projectView: ProjectView; + let workspaceMock: any; const description = ''; @@ -57,6 +58,15 @@ describe('Integration tests GanacheCommands', () => { getItemsMock.returns(serviceItems); loadStateMock = sinon.stub(TreeManager, 'loadState'); loadStateMock.returns(serviceItems); + // is this enough? + workspaceMock = sinon.stub(vscode.workspace, 'workspaceFolders'); + workspaceMock.value([ + { + uri: vscode.Uri.file('testy'), + index: 0, + name: 'name', + }, + ]); projectView = new ProjectView(new LocalProject('test consortium', defaultPort, options, description)); @@ -65,6 +75,7 @@ describe('Integration tests GanacheCommands', () => { }); afterEach(() => { + // workspaceMock.restore(); sinon.restore(); }); diff --git a/test/commands/GanacheCommands.test.ts b/test/commands/GanacheCommands.test.ts index 152248b7..f26e49dc 100644 --- a/test/commands/GanacheCommands.test.ts +++ b/test/commands/GanacheCommands.test.ts @@ -5,15 +5,15 @@ import assert from 'assert'; import {ChildProcess} from 'child_process'; import sinon from 'sinon'; import {commands, OutputChannel, QuickPickItem, window} from 'vscode'; -import {GanacheCommands} from '../../src/commands'; -import {Constants, RequiredApps} from '../../src/Constants'; +import {GanacheCommands} from '@/commands'; +import {Constants, RequiredApps} from '@/Constants'; import * as userInteraction from '../../src/helpers/userInteraction'; -import {required} from '../../src/helpers/required'; +import {required} from '@/helpers/required'; import * as shell from '../../src/helpers/shell'; -import {IExtensionItem, LocalProject, LocalService, Service, TLocalProjectOptions} from '../../src/Models/TreeItems'; -import {GanacheService, TreeManager} from '../../src/services'; +import {IExtensionItem, LocalProject, LocalService, Service, TLocalProjectOptions} from '@/Models/TreeItems'; +import {GanacheService, TreeManager} from '@/services'; import * as GanacheServiceClient from '../../src/services/ganache/GanacheServiceClient'; -import {ProjectView} from '../../src/ViewItems'; +import {ProjectView} from '@/ViewItems'; import {TestConstants} from '../TestConstants'; const description = ''; diff --git a/test/commands/ProjectCommand.test.ts b/test/commands/ProjectCommand.test.ts index 398148db..fb6540d2 100644 --- a/test/commands/ProjectCommand.test.ts +++ b/test/commands/ProjectCommand.test.ts @@ -6,12 +6,12 @@ import fs from 'fs-extra'; import rewire from 'rewire'; import sinon from 'sinon'; import {CancellationToken, Progress, ProgressOptions, window, workspace} from 'vscode'; -import {Constants, RequiredApps} from '../../src/Constants'; +import {Constants, RequiredApps} from '@/Constants'; import * as helpers from '../../src/helpers/'; -import {required} from '../../src/helpers/required'; +import {required} from '@/helpers/required'; import * as userInteraction from '../../src/helpers/userInteraction'; -import {CancellationEvent} from '../../src/Models'; -import {Output} from '../../src/Output'; +import {CancellationEvent} from '@/Models'; +import {Output} from '@/Output'; import * as vscode from 'vscode'; enum ProjectType { diff --git a/test/commands/SdkCoreCommands.test.ts b/test/commands/SdkCoreCommands.test.ts new file mode 100644 index 00000000..dd87b98f --- /dev/null +++ b/test/commands/SdkCoreCommands.test.ts @@ -0,0 +1,124 @@ +// Copyright (c) 2022. Consensys Software Inc. All rights reserved. +// Licensed under the MIT license. + +import * as AW from '@/helpers/AbstractWorkspace'; +import {expect} from 'chai'; +import glob from 'glob'; +import fs from 'fs'; +import sinon from 'sinon'; +import {sdkCoreCommands} from '@/commands/SdkCoreCommands'; +import {Uri, workspace, WorkspaceFolder} from 'vscode'; +import {TruffleCommands} from '@/commands/TruffleCommands'; +import * as HardhatCommands from '@/commands/HardhatCommands'; + +describe('SDK Core Commands', () => { + const sandbox = sinon.createSandbox(); + + let globStub: any; + let fsStub: any; + let truffleBuildStub: any; + let hardhatBuildStub: any; + let workspaces: WorkspaceFolder[] = []; + + const setupTestScenario = function (testFolderName: string, globPattern: any) { + const foundFile = testFolderName + '/someconfig.file'; + workspaces.push({ + uri: Uri.file(testFolderName), + index: 0, + name: testFolderName + '-name', + }); + // just return a truffle one... we only want to return 1 + globStub.withArgs().callsFake(function (pattern: string): string[] { + return pattern.includes(globPattern) ? [foundFile] : []; + }); + }; + + let extensionAdapterSpy: any; + + beforeEach(async () => { + //setup the mockery... + const getWorkspsaceFolderStub = sandbox.stub(workspace, 'getWorkspaceFolder'); + getWorkspsaceFolderStub.callsFake((_uri) => workspaces[0]); + + const workspaceFolders = sandbox.stub(workspace, 'workspaceFolders'); + workspaceFolders.value(workspaces); + + truffleBuildStub = sandbox.stub(TruffleCommands, 'buildContracts'); + truffleBuildStub.returns(); + + hardhatBuildStub = sandbox.stub(HardhatCommands, 'buildContracts'); + hardhatBuildStub.returns(); + + fsStub = sandbox.stub(fs, 'lstatSync'); + fsStub.returns({ + isFile() { + return true; + }, + }); + + globStub = sandbox.stub(glob, 'sync'); + extensionAdapterSpy = sandbox.spy(sdkCoreCommands, 'getExtensionAdapter'); + }); + + afterEach(async () => { + workspaces = []; + sandbox.restore(); + }); + + describe('SDK Commands - Project Resolution', () => { + it('will find correct command to build - truffle', async function () { + // given - truffle workspace + const wsFolder = 'truffle-project-1'; + const buildFolder = Uri.file(wsFolder); + setupTestScenario(wsFolder, AW.TRUFFLE_CONFIG_GLOB); + + // when I call build + await sdkCoreCommands.build(buildFolder); + + // then the correct methods should have been called. + expect(extensionAdapterSpy.calledOnceWith(AW.WorkspaceType.TRUFFLE)).to.be.true; + expect(hardhatBuildStub.notCalled).to.be.true; + expect(truffleBuildStub.calledOnce).to.be.true; + // console.log(`args: `, {args: truffleBuildStub.firstCall.args}); // WORKSPACE args[0] + expect(truffleBuildStub.firstCall.args[0].path).to.be.eq('/truffle-project-1'); + }); + + it('will find correct command to build - hardhat', async function () { + // given - hardhat workspace + const wsFolder = 'hardhat-project-1'; + const buildFolder = Uri.file(wsFolder); + setupTestScenario(wsFolder, AW.HARDHAT_CONFIG_GLOB); + + // when I call build + await sdkCoreCommands.build(buildFolder); + + // then the correct methods should have been called. + expect(extensionAdapterSpy.calledOnceWith(AW.WorkspaceType.HARDHAT)).to.be.true; + expect(truffleBuildStub.notCalled).to.be.true; + expect(hardhatBuildStub.calledOnce).to.be.true; + // console.log(`args: `, {args: hardhatBuildStub.firstCall.args}); // WORKSPACE args[0] + expect(hardhatBuildStub.firstCall.args[1].path).to.be.eq('/hardhat-project-1'); + }); + + it('will find correct command to build - unknown', async function () { + const wsFolder = 'some-empty-folder'; + const buildFolder = Uri.file(wsFolder); + workspaces.push({ + uri: buildFolder, + index: 0, + name: wsFolder + '-name', + }); + // return 0 workspaces with actual configs in them. + globStub.withArgs().returns([]); + + // when I call build - with no contract and ultimately no configured framework workspace... + await sdkCoreCommands.build(); + + // test the unknown one was also triggered... + expect(extensionAdapterSpy.calledOnceWith(AW.WorkspaceType.UNKNOWN)).to.be.true; + // then unknown will be called. not the others. + expect(truffleBuildStub.notCalled).to.be.true; + expect(hardhatBuildStub.notCalled).to.be.true; + }); + }); +}); diff --git a/test/debugAdapter/debugSession.test.ts b/test/debugAdapter/debugSession.test.ts index ff941dcc..e49f1d74 100644 --- a/test/debugAdapter/debugSession.test.ts +++ b/test/debugAdapter/debugSession.test.ts @@ -7,10 +7,10 @@ import {join as pathJoin} from 'path'; import sinon from 'sinon'; import {StoppedEvent} from '@vscode/debugadapter'; import {DebugProtocol} from '@vscode/debugprotocol'; -import {GET_CURRENT_INSTRUCTION, GET_INSTRUCTIONS} from '../../src/debugAdapter/constants/debugSessionCommands'; -import {SolidityDebugSession} from '../../src/debugAdapter/debugSession'; -import {DebuggerTypes} from '../../src/debugAdapter/models/debuggerTypes'; -import {IInstruction} from '../../src/debugAdapter/models/IInstruction'; +import {GET_CURRENT_INSTRUCTION, GET_INSTRUCTIONS} from '@/debugAdapter/constants/debugSessionCommands'; +import {SolidityDebugSession} from '@/debugAdapter/debugSession'; +import {DebuggerTypes} from '@/debugAdapter/models/debuggerTypes'; +import {IInstruction} from '@/debugAdapter/models/IInstruction'; import RuntimeInterface from '../../src/debugAdapter/runtimeInterface'; import {SolidityDebugSessionClient} from './SolidityDebugSessionClient'; @@ -22,7 +22,7 @@ describe('DebugSession unit tests', () => { it("shouldn't contain vscode module as a dependency", (done) => { // vscode module can be resolved inside of the extension without any issues // that's why we should spawn independent node process to check - const debugSessionModule = pathJoin(__dirname, '../../src/debugAdapter/debugSession.js'); + const debugSessionModule = pathJoin(__dirname, '../../src/debugAdapter/debugSession.ts'); const debugSessionResolvingProcess = spawn(process.execPath, [debugSessionModule]); debugSessionResolvingProcess.on('close', () => { done(); diff --git a/test/mocks/MockExtensionContext.ts b/test/mocks/MockExtensionContext.ts new file mode 100644 index 00000000..0ce4f43f --- /dev/null +++ b/test/mocks/MockExtensionContext.ts @@ -0,0 +1,20 @@ +// Copyright (c) 2022. Consensys Software Inc. All rights reserved. +// Licensed under the MIT license. + +import {Disposable, ExtensionContext} from 'vscode'; + +type ExtensionContextPlus = ExtensionContext & Pick; + +export class MockExtensionContext implements Partial { + subscriptions: Disposable[] = []; + + asAbsolutePath = (relativePath: string): string => relativePath; + + static new(): ExtensionContextPlus { + return new this() as unknown as ExtensionContextPlus; + } + + teardown() { + this.subscriptions.forEach((x) => x.dispose()); + } +} diff --git a/test/mocks/MockMemento.ts b/test/mocks/MockMemento.ts new file mode 100644 index 00000000..54caebaf --- /dev/null +++ b/test/mocks/MockMemento.ts @@ -0,0 +1,31 @@ +// Copyright (c) 2022. Consensys Software Inc. All rights reserved. +// Licensed under the MIT license. + +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-prototype-builtins */ +import {Memento} from 'vscode'; + +export class MockMemento implements Memento { + constructor(private dict: {[id: string]: any} = {}) {} + + // // _value must be named this way in order to match vscode's memento + // private _value: Record = {}; + + public get(key: any, defaultValue?: any): any; + public get(key: string, defaultValue?: T): T { + const exists = this.dict.hasOwnProperty(key); + return exists ? this.dict[key] : (defaultValue! as any); + } + + public update(key: string, value: any): Thenable { + this.dict[key] = value; + return Promise.resolve(); + } + public clear() { + this.dict = {}; + } + + keys(): readonly string[] { + return Object.keys(this.dict); + } +} diff --git a/test/required.test.ts b/test/required.test.ts index 9a17f278..0225a2e1 100644 --- a/test/required.test.ts +++ b/test/required.test.ts @@ -6,9 +6,10 @@ import rewire from 'rewire'; import sinon from 'sinon'; import uuid from 'uuid'; import * as vscode from 'vscode'; -import {RequiredApps} from '../src/Constants'; -import * as helpers from '@/helpers/workspace'; -import * as commands from '../src/helpers/command'; +//import * as vscode from './vscode'; +import {RequiredApps} from '@/Constants'; +import * as helpers from '@/helpers/WorkspaceHelpers'; +import * as commands from '@/helpers/command'; import {TestConstants} from './TestConstants'; const nodeValidVersion: commands.ICommandResult = { @@ -47,6 +48,19 @@ describe('Required helper', () => { let executeCommandMock: any; beforeEach(() => { + const workspaces = [ + { + uri: vscode.Uri.file('testiy'), + index: 0, + name: 'name', + }, + ]; + const getWorkspsaceFolderStub = sinon.stub(vscode.workspace, 'getWorkspaceFolder'); + getWorkspsaceFolderStub.callsFake((_uri) => workspaces[0]); + + const workspaceFolders = sinon.stub(vscode.workspace, 'workspaceFolders'); + workspaceFolders.value(workspaces); + requiredRewire = rewire('../src/helpers/required'); tryExecuteCommandMock = sinon.stub(commands, 'tryExecuteCommand'); getWorkspaceRootMock = sinon.stub(helpers, 'getWorkspaceRoot'); diff --git a/test/vscode.ts b/test/vscode.ts index 354a1da3..b362c122 100644 --- a/test/vscode.ts +++ b/test/vscode.ts @@ -147,7 +147,7 @@ export const workspace = { } as any; }, - getWorkspaceFolder: function (_uri: Uri): WorkspaceFolder | undefined { + getWorkspaceFolder(_uri: Uri): WorkspaceFolder | undefined { return workspace.workspaceFolders![0]; }, diff --git a/test/workspace.test.ts b/test/workspace.test.ts index 94862491..b5b162ed 100644 --- a/test/workspace.test.ts +++ b/test/workspace.test.ts @@ -1,101 +1,230 @@ // Copyright (c) Consensys Software Inc. All rights reserved. // Licensed under the MIT license. -import assert from 'assert'; +import * as AW from '@/helpers/AbstractWorkspace'; +import {AbstractWorkspace, resolveAllWorkspaces, WorkspaceType} from '@/helpers/AbstractWorkspace'; +import * as userInteraction from '@/helpers/userInteraction'; +import chai from 'chai'; +import chai_as_promised from 'chai-as-promised'; +import fs from 'fs'; +import glob from 'glob'; import sinon from 'sinon'; -import * as vscode from 'vscode'; -import {getTruffleWorkspace, getWorkspaceRoot} from '@/helpers/workspace'; - -describe('workspace', () => { - const testWorkspaceFolder: any[] = [ - { - uri: { - fsPath: 'testPath1', - }, - }, - { - uri: { - fsPath: 'testPath2', +import {QuickPickItem, Uri, workspace, WorkspaceFolder} from 'vscode'; + +chai.use(chai_as_promised); +const expect = chai.expect; + +type QuickPickType = {workspace: AbstractWorkspace; description: string; label: string; detail: string}; + +describe('Workspace - WorkspaceForUri Tests', () => { + const sandbox = sinon.createSandbox(); + + let globStub: sinon.SinonStub; + let fsStub: sinon.SinonStub; + let quickPickStub: sinon.SinonStub>; + + let workspaces: WorkspaceFolder[] = []; + + const pushWorkspace = (testFolderName: string) => + workspaces.push({ + uri: Uri.file(testFolderName), + index: workspace.workspaceFolders ? workspace.workspaceFolders!.length : 0, + name: testFolderName + '-name', + }); + + const aw1 = AW.AbstractWorkspace.createUnknownWorkspace(Uri.file('/dev/unknown-aw1')); + const aw2 = AW.AbstractWorkspace.createWorkspaceFromConfigPath( + '/dev/truffle-aw2/bleh2.conf.js', + WorkspaceType.TRUFFLE + ); + + beforeEach(async () => { + //setup the mockery... + const getWorkspsaceFolderStub = sandbox.stub(workspace, 'getWorkspaceFolder'); + getWorkspsaceFolderStub.callsFake((_uri) => workspaces[0]); + + const workspaceFolders = sandbox.stub(workspace, 'workspaceFolders'); + workspaceFolders.value(workspaces); + + quickPickStub = sandbox.stub(userInteraction, 'showQuickPick'); + fsStub = sandbox.stub(fs, 'lstatSync'); + fsStub.returns({ + isFile() { + return true; }, - }, - ]; + }); - let workspaceMock: sinon.SinonStub; + globStub = sandbox.stub(glob, 'sync'); + globStub.withArgs().returns([]); + }); - beforeEach(() => { - workspaceMock = sinon.stub(vscode.workspace, 'workspaceFolders'); + afterEach(async () => { + sandbox.restore(); + workspaces = []; }); - afterEach(() => { - workspaceMock.restore(); + const setupTestScenario = function (testFolderName: string, globPattern: any) { + const foundFile = testFolderName + '/someconfig.file'; + pushWorkspace(testFolderName); + // just return a matching one... we only want to return 1 + globStub.withArgs().callsFake(function (pattern: string): string[] { + return pattern.includes(globPattern) ? [foundFile] : []; + }); + }; + + it('will resolve workspace correctly - in a truffle folder.', async function () { + //given - I have the default value set. + const wsFolder = '/dev/some/thing/truffle-test'; + setupTestScenario(wsFolder, AW.TRUFFLE_CONFIG_GLOB); + + // when I call + const workspaceRet = await AW.getWorkspaceForUri(Uri.file(wsFolder)); + // then the workspace will be correct + expect(workspaceRet.dirName).to.be.eq('truffle-test'); + expect(workspaceRet.workspaceType).to.be.eq(AW.WorkspaceType.TRUFFLE); }); - describe('getWorkspaceRoot', () => { - it('should throw exception when no workspace is opened', () => { - // Arrange - workspaceMock.value(undefined); + it('will resolve workspace correctly - in a hardhat folder.', async function () { + //given - I have the default value set. + const wsFolder = '/dev/blah/blah/hardhat-test'; + setupTestScenario(wsFolder, AW.HARDHAT_CONFIG_GLOB); - // Act and assert - assert.throws(getWorkspaceRoot, /Workspace root should be defined/); - }); + // when I call the workspace resolver... + const workspaceRet = await AW.getWorkspaceForUri(Uri.file(wsFolder)); - it('should throw exception when workspace is empty', () => { - // Arrange - workspaceMock.value([]); + // then the Truffle Instances will be called. + expect(workspaceRet.dirName).to.be.eq('hardhat-test'); + expect(workspaceRet.workspaceType).to.be.eq(AW.WorkspaceType.HARDHAT); + }); - // Act and assert - assert.throws(getWorkspaceRoot, /Workspace root should be defined/); - }); + it("will do nothing when it can't find a directory.", async function () { + // given this base folder... + const wsFolder = '/dev/some-empty-folder'; + pushWorkspace(wsFolder); - it('should return `undefined` when no workspace is opened and has `ignoreException`', () => { - // Arrange - workspaceMock.value(undefined); + // when I call the workspace resolver... + const workspaceRet = await AW.getWorkspaceForUri(); + // then + expect(workspaceRet.dirName).to.be.eq('some-empty-folder'); + expect(workspaceRet.workspaceType).to.be.eq(AW.WorkspaceType.UNKNOWN); + }); - // Act - const result = getWorkspaceRoot(true); + it('will show quickpick if multiple workspace found - URI passed in.', async function () { + // given we get no workspaces back + const wsFolder = '/blah/shmah/some-empty-folder'; + pushWorkspace(wsFolder); + sandbox.stub(AW, 'findWorkspaces').returns([aw1, aw2]); + quickPickStub.resolves({workspace: aw1} as QuickPickType); + + // when I try and get the workspace + const workspaceRet = await AW.getWorkspaceForUri(Uri.file(wsFolder)); + // then I am going to hit the quickpick and return the one from there... + expect(workspaceRet).to.be.not.undefined; + expect(workspaceRet.workspace.path).to.be.eq('/dev/unknown-aw1'); + expect(workspaceRet.dirName).to.be.eq('unknown-aw1'); + expect(workspaceRet.workspaceType).to.be.eq(WorkspaceType.UNKNOWN); + expect(workspaceRet.configName).to.be.eq(aw1.configName); + expect(quickPickStub.calledOnce).to.be.true; + }); - // Assert - assert.strictEqual(result, undefined); + it('will show quickpick if multiple workspace found - no URI passed in.', async function () { + // given - I despair about testing in JS land and Sinon is about as useful as a chocolate fireguard... + pushWorkspace('/dev/some-empty-folder'); + globStub.withArgs().callsFake(function (pattern: string): string[] { + if (pattern.includes('hardhat')) { + return ['hardhat1/hh-config.ts', 'hardhat2/hh-config.ts']; + } + return []; }); + quickPickStub.resolves({workspace: aw2} as QuickPickType); - it('should return `undefined` when workspace is empty and has `ignoreException`', () => { - // Arrange - workspaceMock.value([]); + // when I try and get the workspace + const workspaceRet = await AW.getWorkspaceForUri(); - // Act - const result = getWorkspaceRoot(true); + // then I am going to hit the quickpick and return the one from there... + expect(workspaceRet).to.be.not.undefined; + expect(workspaceRet.workspaceType).to.be.eq(aw2.workspaceType); + expect(workspaceRet.workspace.fsPath).to.be.eq(aw2.workspace.fsPath); + expect(quickPickStub.calledOnce).to.be.true; + }); - // Assert - assert.strictEqual(result, undefined); - }); + it('will throw error when no workspaces', async function () { + expect(AW.getWorkspaceForUri()).to.eventually.be.rejectedWith(Error, 'Workspace root should be defined'); + }); - it('should return the first workspace root path', async () => { - // Arrange - workspaceMock.value(testWorkspaceFolder); + it('will return first when only 1 workspace found - no uri passed', async function () { + //given - I have the default value set. + const wsFolder = '/dev/hardhat-test'; + setupTestScenario(wsFolder, AW.HARDHAT_CONFIG_GLOB); - // Act - const result = getWorkspaceRoot(); + // when I try and get the workspace + const workspaceRet = await AW.getWorkspaceForUri(); - // Assert - assert.strictEqual(result, testWorkspaceFolder[0].uri.fsPath); - }); + // then I am going to hit the quickpick and return the one from there... + expect(workspaceRet).to.be.not.undefined; + expect(workspaceRet.workspaceType).to.be.eq(WorkspaceType.HARDHAT); + expect(quickPickStub.notCalled).to.be.true; }); - describe('getTruffleWorkspace', () => { - it('should reject when no workspace is opened', () => { - // Arrange - workspaceMock.value(undefined); + it('will resolve all workspaces - 1 workspace - [UNKNOWN] - includeUnknown=true', async function () { + // given + pushWorkspace('/dev/test-folder-1'); + // when + const workspaces = resolveAllWorkspaces(true); + // then + expect(workspaces).to.have.length(1); + expect(workspaces[0].dirName).to.be.eq('test-folder-1'); + expect(workspaces[0].workspaceType).to.be.eq(WorkspaceType.UNKNOWN); + }); - // Act and assert - assert.rejects(getTruffleWorkspace, /Workspace root should be defined/); - }); + it('will resolve all workspaces - 1 workspace - [UNKNOWN] - includeUnknown=false', async function () { + // given + pushWorkspace('/dev/test-folder-1'); + // when + const workspaces = resolveAllWorkspaces(false); + // then + expect(workspaces).to.have.length(0); + }); - it('should reject when workspace is empty', () => { - // Arrange - workspaceMock.value([]); + it('will resolve all workspaces - 2 workspaces - [UNKNOWN, TRUFFLE] - includeUnknown=true', async function () { + // given + pushWorkspace('/dev/test-folder-1'); + pushWorkspace('/dev/test-folder-2'); + // map one to be truffle + globStub + .withArgs(sinon.match((arg: string) => new RegExp('.*?test-folder-2.*?truffle-config.*').test(arg))) + .returns(['test-folder-2/gosh-this-is-hard.js']); + globStub.returns([]); + + // when + const workspaces = resolveAllWorkspaces(true); + // then + expect(workspaces).to.have.length(2); + + expect(workspaces[0].dirName).to.be.eq('test-folder-1'); + expect(workspaces[0].workspaceType).to.be.eq(WorkspaceType.UNKNOWN); + + expect(workspaces[1].dirName).to.be.eq('test-folder-2'); + expect(workspaces[1].configName).to.be.eq('gosh-this-is-hard.js'); + expect(workspaces[1].workspaceType).to.be.eq(WorkspaceType.TRUFFLE); + }); - // Act and assert - assert.rejects(getTruffleWorkspace, /Workspace root should be defined/); - }); + it('will resolve all workspaces - 2 workspaces - [UNKNOWN, TRUFFLE] - includeUnknown=false', async function () { + // given + pushWorkspace('/dev/test-folder-1'); + pushWorkspace('/dev/test-folder-2'); + // map one to be truffle + globStub + .withArgs(sinon.match((arg: string) => new RegExp('.*?test-folder-2.*?truffle-config.*').test(arg))) + .returns(['test-folder-2/gosh-this-is-hard.js']); + globStub.returns([]); + + // when + const workspaces = resolveAllWorkspaces(false); + // then + expect(workspaces).to.have.length(1); + expect(workspaces[0].dirName).to.be.eq('test-folder-2'); + expect(workspaces[0].configName).to.be.eq('gosh-this-is-hard.js'); + expect(workspaces[0].workspaceType).to.be.eq(WorkspaceType.TRUFFLE); }); }); diff --git a/yarn.lock b/yarn.lock index 9806927d..a977ecf5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -955,29 +955,42 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.7.0": +"@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" -"@sinonjs/formatio@^3.2.1": - version "3.2.2" - resolved "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz" - integrity sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ== +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== dependencies: - "@sinonjs/commons" "^1" - "@sinonjs/samsam" "^3.1.0" + type-detect "4.0.8" -"@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.3": - version "3.3.3" - resolved "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz" - integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== +"@sinonjs/fake-timers@^7.0.4": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== dependencies: - "@sinonjs/commons" "^1.3.0" - array-from "^2.1.1" - lodash "^4.17.15" + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/fake-timers@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" + integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/samsam@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-7.0.1.tgz#5b5fa31c554636f78308439d220986b9523fc51f" + integrity sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw== + dependencies: + "@sinonjs/commons" "^2.0.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" "@sinonjs/text-encoding@^0.7.1": version "0.7.1" @@ -2020,6 +2033,18 @@ resolved "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz" integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== +"@types/chai-as-promised@^7.1.5": + version "7.1.5" + resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz#6e016811f6c7a64f2eed823191c3a6955094e255" + integrity sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ== + dependencies: + "@types/chai" "*" + +"@types/chai@*", "@types/chai@^4.3.4": + version "4.3.4" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4" + integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw== + "@types/connect@*": version "3.4.35" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" @@ -2329,10 +2354,17 @@ "@types/mime" "*" "@types/node" "*" -"@types/sinon@^7.0.11": - version "7.5.2" - resolved "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.2.tgz" - integrity sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg== +"@types/sinon@^10.0.13": + version "10.0.13" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.13.tgz#60a7a87a70d9372d0b7b38cc03e825f46981fb83" + integrity sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ== + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "8.1.2" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz#bf2e02a3dbd4aecaf95942ecd99b7402e03fad5e" + integrity sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA== "@types/source-map@^0.5.2": version "0.5.7" @@ -2822,11 +2854,6 @@ amdefine@>=0.0.4: resolved "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= -ansi-colors@3.2.3: - version "3.2.3" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz" - integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== - ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" @@ -2854,11 +2881,6 @@ ansi-regex@^3.0.0: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz" - integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== - ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" @@ -2869,7 +2891,7 @@ ansi-styles@^2.2.1: resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: 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== @@ -3077,11 +3099,6 @@ array-flatten@1.1.1: resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-from@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz" - integrity sha1-z+nYwmYoudxa7MYqn12PHzUsEZU= - array-ify@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz" @@ -3146,6 +3163,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" @@ -3763,7 +3785,7 @@ camelcase@^4.1.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -3808,6 +3830,26 @@ cbor@^5.1.0, cbor@^5.2.0: bignumber.js "^9.0.1" nofilter "^1.0.4" +chai-as-promised@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" + integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== + dependencies: + check-error "^1.0.2" + +chai@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^4.1.2" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + chainsaw@~0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz" @@ -3880,6 +3922,11 @@ chardet@^0.4.0: resolved "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz" integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== + cheerio-select@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz" @@ -4004,15 +4051,6 @@ cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" - cliui@^7.0.2: version "7.0.4" resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" @@ -4508,14 +4546,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@3.2.6: - version "3.2.6" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -debug@4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4551,7 +4582,7 @@ decamelize-keys@^1.1.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.2.0: +decamelize@^1.1.0, decamelize@^1.1.1: version "1.2.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -4638,6 +4669,13 @@ dedent@^0.7.0: resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +deep-eql@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.2.tgz#270ceb902f87724077e6f6449aed81463f42fc1c" + integrity sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w== + dependencies: + type-detect "^4.0.0" + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -4666,7 +4704,7 @@ deferred-leveldown@~5.3.0: abstract-leveldown "~6.2.1" inherits "^2.0.3" -define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4: +define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz" integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== @@ -4758,11 +4796,6 @@ detect-libc@^1.0.2: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== -diff@3.5.0, diff@^3.5.0: - version "3.5.0" - resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - diff@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" @@ -4773,6 +4806,11 @@ diff@^4.0.1: resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diff@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" @@ -4964,11 +5002,6 @@ emoji-regex@^10.1.0: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.1.0.tgz" integrity sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg== -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" @@ -5040,7 +5073,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.5, es-abstract@^1.20.0: +es-abstract@^1.19.0, es-abstract@^1.19.5, es-abstract@^1.20.0: version "1.20.1" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz" integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== @@ -5119,16 +5152,16 @@ escape-html@~1.0.3: resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.4, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.4, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + escodegen@1.8.x: version "1.8.1" resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz" @@ -5854,13 +5887,6 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" -find-up@3.0.0, find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" @@ -5884,6 +5910,13 @@ find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" @@ -5910,13 +5943,6 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" -flat@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz" - integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== - dependencies: - is-buffer "~2.0.3" - flat@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" @@ -6183,11 +6209,16 @@ get-caller-file@^1.0.1: resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + get-installed-path@^2.0.3: version "2.1.1" resolved "https://registry.npmjs.org/get-installed-path/-/get-installed-path-2.1.1.tgz" @@ -6305,18 +6336,6 @@ glob-to-regexp@^0.4.1: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@7.1.3: - version "7.1.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@7.2.0, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.0" resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" @@ -6600,7 +6619,7 @@ has-symbol-support-x@^1.4.1: resolved "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz" integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== -has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -7051,7 +7070,7 @@ is-buffer@^1.1.5: resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.5, is-buffer@~2.0.3: +is-buffer@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== @@ -7493,14 +7512,6 @@ js-tokens@^3.0.2: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@3.13.1: - version "3.13.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@3.x, js-yaml@^3.13.1, js-yaml@^3.9.1: version "3.14.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" @@ -7990,6 +8001,11 @@ lodash.assign@^4.0.3, lodash.assign@^4.0.6: resolved "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz" integrity sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw== +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" @@ -8005,13 +8021,6 @@ lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17. resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@2.2.0, log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - log-symbols@4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" @@ -8027,6 +8036,13 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + log-update@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz" @@ -8041,18 +8057,6 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== -lolex@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz" - integrity sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg== - -lolex@^5.0.1: - version "5.1.2" - resolved "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz" - integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== - dependencies: - "@sinonjs/commons" "^1.7.0" - long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -8065,6 +8069,13 @@ loose-envify@^1.1.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +loupe@^2.3.1: + version "2.3.4" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" + integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== + dependencies: + get-func-name "^2.0.0" + lower-case-first@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz" @@ -8337,13 +8348,6 @@ minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - minimatch@4.2.1: version "4.2.1" resolved "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz" @@ -8351,6 +8355,13 @@ minimatch@4.2.1: dependencies: brace-expansion "^1.1.7" +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + minimatch@^5.0.1: version "5.1.0" resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz" @@ -8407,13 +8418,6 @@ mkdirp@*, mkdirp@^1.0.4: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@0.5.4: - version "0.5.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz" - integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== - dependencies: - minimist "^1.2.5" - mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" @@ -8458,34 +8462,32 @@ mocha@9.2.2: yargs-parser "20.2.4" yargs-unparser "2.0.0" -mocha@^6.2.3: - version "6.2.3" - resolved "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz" - integrity sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg== +mocha@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.1.0.tgz#dbf1114b7c3f9d0ca5de3133906aea3dfc89ef7a" + integrity sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg== dependencies: - ansi-colors "3.2.3" + ansi-colors "4.1.1" browser-stdout "1.3.1" - 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 "2.2.0" - minimatch "3.0.4" - mkdirp "0.5.4" - ms "2.1.1" - node-environment-flags "1.0.5" - 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" mock-fs@^4.1.0: version "4.14.0" @@ -8507,11 +8509,6 @@ ms@2.0.0: resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" @@ -8603,6 +8600,11 @@ nanoid@3.3.1: resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" @@ -8666,15 +8668,15 @@ nice-try@^1.0.4: resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nise@^1.5.2: - version "1.5.3" - resolved "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz" - integrity sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ== +nise@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.2.tgz#a7b8909c216b3491fd4fc0b124efb69f3939b449" + integrity sha512-+gQjFi8v+tkfCuSCxfURHLhRhniE/+IaYbIphxAN2JRR9SHKhY8hgXpaXiYfHdw+gcGe4buxgbprBQFab9FkhA== dependencies: - "@sinonjs/formatio" "^3.2.1" + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers" "^7.0.4" "@sinonjs/text-encoding" "^0.7.1" just-extend "^4.0.2" - lolex "^5.0.1" path-to-regexp "^1.7.0" no-case@^2.2.0, no-case@^2.3.2: @@ -8699,14 +8701,6 @@ node-cleanup@^2.1.2: resolved "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz" integrity sha1-esGavSl+Caf3KnFUXZUbUX5N3iw= -node-environment-flags@1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz" - integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== - dependencies: - object.getownpropertydescriptors "^2.0.3" - semver "^5.7.0" - node-fetch@2.6.7, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -8956,7 +8950,7 @@ object-inspect@^1.12.0, object-inspect@^1.9.0: resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-keys@^1.0.11, object-keys@^1.1.1: +object-keys@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -8968,16 +8962,6 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - object.assign@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" @@ -8988,15 +8972,6 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" -object.getownpropertydescriptors@^2.0.3: - version "2.1.3" - resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz" - integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - object.pick@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" @@ -9366,6 +9341,11 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + pause-stream@0.0.11: version "0.0.11" resolved "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz" @@ -10227,11 +10207,6 @@ require-main-filename@^1.0.1: resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz" integrity sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug== -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - require-uncached@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz" @@ -10540,7 +10515,7 @@ semver-compare@^1.0.0: resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.7.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -10737,18 +10712,17 @@ simple-git@^1.85.0: dependencies: debug "^4.0.1" -sinon@^7.3.2: - version "7.5.0" - resolved "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz" - integrity sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q== +sinon@^14.0.2: + version "14.0.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-14.0.2.tgz#585a81a3c7b22cf950762ac4e7c28eb8b151c46f" + integrity sha512-PDpV0ZI3ZCS3pEqx0vpNp6kzPhHrLx72wA0G+ZLaaJjLIYeE0n8INlgaohKuGy7hP0as5tbUd23QWu5U233t+w== dependencies: - "@sinonjs/commons" "^1.4.0" - "@sinonjs/formatio" "^3.2.1" - "@sinonjs/samsam" "^3.3.3" - diff "^3.5.0" - lolex "^4.2.0" - nise "^1.5.2" - supports-color "^5.5.0" + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers" "^9.1.2" + "@sinonjs/samsam" "^7.0.1" + diff "^5.0.0" + nise "^5.1.2" + supports-color "^7.2.0" slash@^3.0.0: version "3.0.0" @@ -11040,7 +11014,7 @@ string-argv@^0.1.1: resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.1.2.tgz" integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== -string-width@^1.0.1, "string-width@^1.0.2 || 2": +string-width@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= @@ -11066,15 +11040,6 @@ string-width@^2.1.0, string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - string.prototype.trimend@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz" @@ -11135,13 +11100,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -11197,16 +11155,16 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@2.0.1, strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: 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== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz" @@ -11224,13 +11182,6 @@ sublevel-pouchdb@7.3.0: ltgt "2.2.1" readable-stream "1.1.14" -supports-color@6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz" - integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== - dependencies: - has-flag "^3.0.0" - supports-color@8.1.1, supports-color@^8.0.0: version "8.1.1" resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" @@ -11250,14 +11201,14 @@ supports-color@^3.1.0: dependencies: has-flag "^1.0.0" -supports-color@^5.3.0, supports-color@^5.5.0: +supports-color@^5.3.0: version "5.5.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -11670,9 +11621,9 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@4.0.8: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.18.0: @@ -12382,11 +12333,6 @@ which-module@^1.0.0: resolved "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz" integrity sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ== -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - which-typed-array@^1.1.2: version "1.1.8" resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz" @@ -12399,13 +12345,6 @@ which-typed-array@^1.1.2: has-tostringtag "^1.0.0" is-typed-array "^1.1.9" -which@1.3.1, which@^1.1.1, which@^1.2.10, which@^1.2.14, which@^1.2.9, which@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" @@ -12413,12 +12352,12 @@ which@2.0.2, which@^2.0.1: dependencies: isexe "^2.0.0" -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== +which@^1.1.1, which@^1.2.10, which@^1.2.14, which@^1.2.9, which@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: - string-width "^1.0.2 || 2" + isexe "^2.0.0" wide-align@^1.1.0: version "1.1.5" @@ -12452,6 +12391,11 @@ workerpool@6.2.0: resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz" integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" @@ -12468,15 +12412,6 @@ wrap-ansi@^3.0.1: string-width "^2.1.1" strip-ansi "^4.0.0" -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" @@ -12584,11 +12519,6 @@ y18n@^3.2.1: resolved "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz" integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" @@ -12619,14 +12549,6 @@ yaml@^1.10.0: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@13.1.2, yargs-parser@^13.1.2: - version "13.1.2" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz" - integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" @@ -12650,15 +12572,6 @@ yargs-parser@^21.0.0: resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs-unparser@1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz" - integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== - dependencies: - flat "^4.1.0" - lodash "^4.17.15" - yargs "^13.3.0" - yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" @@ -12669,22 +12582,6 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@13.3.2, yargs@^13.3.0: - version "13.3.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz" - integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.2" - yargs@16.2.0, yargs@^16.1.0: version "16.2.0" resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz"