From 2645ca5122cc98678ab4e1d28268194838fae184 Mon Sep 17 00:00:00 2001 From: tenninebt <5684363+tenninebt@users.noreply.github.com> Date: Wed, 25 Oct 2023 09:12:10 +0200 Subject: [PATCH] refactor + add debounce (close #41) --- .eslintrc.json | 5 +- README.md | 12 ++- package.json | 24 ++++- src/ConfigStore.ts | 7 +- src/CoverageLevel.ts | 10 ++ src/DataProvider.ts | 240 ++++++++++++------------------------------- src/Extension.ts | 7 +- src/TreeNodes.ts | 127 +++++++++++++++++++++++ webpack.config.js | 13 +-- yarn.lock | 22 +++- 10 files changed, 262 insertions(+), 205 deletions(-) create mode 100644 src/CoverageLevel.ts create mode 100644 src/TreeNodes.ts diff --git a/.eslintrc.json b/.eslintrc.json index 776480b..c1323ed 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,7 @@ { "env": { "browser": false, - "es2021": true + "es2022": true }, "extends": ["standard-with-typescript", "prettier"], "overrides": [], @@ -11,7 +11,8 @@ "project": "tsconfig.json" }, "rules": { - "@typescript-eslint/strict-boolean-expressions": "off" + "@typescript-eslint/strict-boolean-expressions": "off", + "@typescript-eslint/no-confusing-void-expression": "off" }, "ignorePatterns": ["*.js", "**/node_modules/"] } diff --git a/README.md b/README.md index 4290a39..8445dee 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,14 @@ This extension adds a tree view to the test view container. It shows the coverag This extension contributes the following settings: -* `koverage.coverageFileNames`: coverage file names to look for, default: ["lcov.info", "cov.xml", "coverage.xml","jacoco.xml"] -* `koverage.coverageFilePaths`: coverage paths where coverage files are located, default: ["coverage"] -* `koverage.lowCoverageThreshold`: Percentage under which, the coverage is considered too low (Renders as Error icon) -* `koverage.sufficientCoverageThreshold`: Percentage above which, the coverage is considered sufficient (Renders as Success icon) +- `koverage.coverageCommand`: Command to run to generate coverage. (Default: "") +- `koverage.autoRefresh`: Whether to watch the coverage files and configurations. (Default: true) +- `koverage.autoRefreshDebounce`: Auto refresh debounce interval in milliseconds. (Default: 3000) +- `koverage.coverageFileNames`: Coverage file names to look for. (Default: ["lcov.info", "cov.xml", "coverage.xml", "jacoco.xml"]) +- `koverage.coverageFilePaths`: Coverage file paths to search in. (Default: ["**"]) +- `koverage.ignoredPathGlobs`: Ignored path globs. (Default: "**/{node_modules,venv,.venv,vendor}/**") +- `koverage.lowCoverageThreshold`: Coverage threshold considered too low. (Default: 50) +- `koverage.sufficientCoverageThreshold`: Coverage threshold considered sufficient. (Default: 70) => lowCoverageThreshold < level < sufficientCoverageThreshold is rendered as Warn icon ## Licencing diff --git a/package.json b/package.json index 325d096..eb36ec4 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "keywords": [ "coverage", "lcov", - "jacoco" + "jacoco", + "clover" ], "repository": "https://github.com/tenninebt/vscode-koverage", "description": "View the code coverage per folder/file in the test view", @@ -31,15 +32,28 @@ "description": "command to run to generate coverage", "scope": "resource" }, + "koverage.autoRefresh": { + "type": "boolean", + "default": true, + "description": "Whether to watch the coverage files and configurations", + "scope": "resource" + }, + "koverage.autoRefreshDebounce": { + "type": "number", + "default": 3000, + "description": "Auto refresh debounce interval in milliseconds", + "scope": "resource" + }, "koverage.coverageFileNames": { "type": "array", "default": [ "lcov.info", "cov.xml", + "clover.xml", "coverage.xml", "jacoco.xml" ], - "description": "coverage file names for the extension to automatically look for", + "description": "coverage file names to look for", "scope": "resource" }, "koverage.coverageFilePaths": { @@ -47,7 +61,7 @@ "default": [ "**" ], - "description": "coverage file paths for the extensions to automatically search in", + "description": "coverage file paths to search in", "scope": "resource" }, "koverage.ignoredPathGlobs": { @@ -121,10 +135,11 @@ } }, "scripts": { + "clean": "rm -rf dist", + "compile": "yarn clean && tsc", "build:prod": "webpack --mode production", "build:dev": "webpack --mode development", "build:watch": "webpack --mode development --watch", - "compile": "tsc", "lint": "eslint ./src" }, "devDependencies": { @@ -140,6 +155,7 @@ "eslint-plugin-n": "^15.7.0", "eslint-plugin-promise": "^6.1.1", "ovsx": "^0.8.0", + "ts-loader": "^9.5.0", "typescript": "^5.0.3", "vscode-test": "^1.3.0", "webpack": "^5.74.0", diff --git a/src/ConfigStore.ts b/src/ConfigStore.ts index 3a91c62..9cec1b9 100644 --- a/src/ConfigStore.ts +++ b/src/ConfigStore.ts @@ -6,6 +6,8 @@ export class ConfigStore { private readonly configurationKey: string = "koverage" private readonly _configChangedNotifier: rx.Subject + public readonly configChangedNotifier: rx.Observable + private readonly _perFolderConfig: Map> public get(workspaceFolder: vscode.WorkspaceFolder): Config { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -14,6 +16,7 @@ export class ConfigStore { constructor(private readonly logger: vscodeLogging.IVSCodeExtLogger) { this._configChangedNotifier = new rx.Subject() + this.configChangedNotifier = this._configChangedNotifier.asObservable() this._perFolderConfig = new Map>() void this.readConfig() @@ -93,10 +96,6 @@ export class ConfigStore { sufficientCoverageThreshold }) } - - public subscribe(next?: () => void, error?: (error: any) => void, complete?: () => void): rx.Subscription { - return this._configChangedNotifier.subscribe(next, error, complete) - } } export class Config { diff --git a/src/CoverageLevel.ts b/src/CoverageLevel.ts new file mode 100644 index 0000000..382bb6c --- /dev/null +++ b/src/CoverageLevel.ts @@ -0,0 +1,10 @@ + +export enum CoverageLevel { + Low = "low", + Medium = "medium", + High = "high" +} + +export class CoverageLevelThresholds { + constructor(public readonly sufficientCoverageThreshold: number, public readonly lowCoverageThreshold: number) { } +} diff --git a/src/DataProvider.ts b/src/DataProvider.ts index 9771de7..2f02b41 100644 --- a/src/DataProvider.ts +++ b/src/DataProvider.ts @@ -1,18 +1,26 @@ import type * as vscodeLogging from "@vscode-logging/logger" import * as fs from "fs" import * as iopath from "path" -import * as cp from "child_process" +import * as childProcess from "child_process" import * as vscode from "vscode" import { type ConfigStore } from "./ConfigStore" import { type CoverageParser } from "./CoverageParser" import { type FilesLoader } from "./FilesLoader" import { type Section as CoverageSection } from "lcov-parse" import { WorkspaceFolderCoverage } from "./WorkspaceFolderCoverageFile" +import { type Subscription, Subject, EMPTY, Observable, throttleTime, merge, map, share } from "rxjs" +import { type BaseNode, type CoverageNode, RootCoverageNode, FolderCoverageNode, FileCoverageNode } from "./TreeNodes" +import { CoverageLevelThresholds } from "./CoverageLevel" + +type RefreshReason = "" | "" | "" | "" | "" export class FileCoverageDataProvider implements vscode.TreeDataProvider, vscode.Disposable { - private coverageWatcher: vscode.FileSystemWatcher - private readonly rootNodeKey: string = "" + private readonly rootNodeKey: string + + private readonly refreshSink: Subject + private readonly refreshObservable: Observable + private readonly refreshSubscription: Subscription constructor( private readonly configStore: ConfigStore, @@ -31,49 +39,24 @@ export class FileCoverageDataProvider implements vscode.TreeDataProvider { - this.refresh("") - }) - } + this.rootNodeKey = "" - dispose(): void { - this.coverageWatcher?.dispose() - } + this.refreshSink = new Subject() + this.refreshObservable = merge(this.refreshSink, this.getConfigObservable(), this.getCoverageObservable()) + .pipe(throttleTime(3000)) - private listenToFileSystem(): void { - if (vscode.workspace.workspaceFolders == null) { - // vscode.window.showInformationMessage('No file coverage in empty workspace'); - return - } - for (const workspaceFolder of vscode.workspace.workspaceFolders) { - const searchPattern = iopath.join( - workspaceFolder.uri.fsPath, - `**${iopath.sep}{${this.configStore.get(workspaceFolder)?.coverageFilePaths?.join(",")}}${iopath.sep}**}` - ) - this.logger.debug(`createFileSystemWatcher(Pattern = ${searchPattern})`) - this.coverageWatcher = vscode.workspace.createFileSystemWatcher(searchPattern) - this.coverageWatcher.onDidChange(() => { - this.refresh("") - }) - this.coverageWatcher.onDidCreate(() => { - this.refresh("") - }) - this.coverageWatcher.onDidDelete(() => { - this.refresh("") - }) - } + this.refreshSubscription = this.refreshObservable.subscribe((reason) => { + this.logger.info(`Refreshing due to ${reason}...`) + this._onDidChangeTreeData.fire(undefined) + }) } - getTreeItem(element: CoverageNode): vscode.TreeItem { + public getTreeItem(element: CoverageNode): vscode.TreeItem { return element } - getChildren(element?: CoverageNode): Thenable { + public getChildren(element?: CoverageNode): Thenable { if (vscode.workspace.workspaceFolders == null) { void vscode.window.showInformationMessage("No file coverage in empty workspace") return Promise.resolve([]) @@ -87,6 +70,10 @@ export class FileCoverageDataProvider implements vscode.TreeDataProvider { return await vscode.window.withProgress( { @@ -112,7 +99,7 @@ export class FileCoverageDataProvider implements vscode.TreeDataProvider((inner_resolve, inner_reject) => { - cp.exec(coverageCommand, { cwd: projectPath }, (err, stdout, stderr) => { + childProcess.exec(coverageCommand, { cwd: projectPath }, (err, stdout, stderr) => { if (err != null) { logger.error(`Error running coverage command: ${err.message}\n${stderr}`) inner_reject(err.message) @@ -129,6 +116,41 @@ export class FileCoverageDataProvider implements vscode.TreeDataProvider { + return this.configStore.configChangedNotifier.pipe(map(() => "")) + } + + private getCoverageObservable(): Observable { + if (vscode.workspace.workspaceFolders == null) { + this.logger.debug("No file coverage in empty workspace") + return EMPTY + } + return merge( + ...vscode.workspace.workspaceFolders.map((workspaceFolder) => { + return new Observable((observer) => { + const searchPattern = iopath.join( + workspaceFolder.uri.fsPath, + `**${iopath.sep}{${this.configStore.get(workspaceFolder)?.coverageFilePaths?.join(",")}}${iopath.sep}**}` + ) + this.logger.info(`createFileSystemWatcher(Pattern = ${searchPattern})`) + const coverageWatcher = vscode.workspace.createFileSystemWatcher(searchPattern) + const fileWatcherEvents = new Observable(observer => { + coverageWatcher.onDidCreate(() => observer.next("")) + coverageWatcher.onDidChange(() => observer.next("")) + coverageWatcher.onDidDelete(() => observer.next("")) + }) + const subscription = fileWatcherEvents.subscribe(observer) + return () => { + this.logger.info(`Dispose FileSystemWatcher(Pattern = ${searchPattern})`) + subscription.unsubscribe() + coverageWatcher.dispose() + } + }) + }) + ).pipe(share()) + + } + private async getRawCoverageData(): Promise> { const coverageData = await this.filesLoader.loadCoverageFiles().then(async (files) => await this.coverageParser.filesToSections(files)) return coverageData @@ -223,144 +245,12 @@ export class FileCoverageDataProvider implements vscode.TreeDataProvider = new vscode.EventEmitter() - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event - - refresh(reason: string): void { - this.logger.debug(`Refreshing due to ${reason}...`) - this._onDidChangeTreeData.fire(undefined) - } -} - -enum CoverageLevel { - Low = "low", - Medium = "medium", - High = "high" -} - -class CoverageLevelThresholds { - constructor(public readonly sufficientCoverageThreshold: number, public readonly lowCoverageThreshold: number) { } -} -export abstract class BaseNode extends vscode.TreeItem { - constructor( - public readonly path: string, - public readonly label: string, - public readonly children: CoverageNode[], - collapsibleState: vscode.TreeItemCollapsibleState | undefined - ) { - super(label, collapsibleState) + public dispose(): void { + this.refreshSubscription.unsubscribe() } - // @ts-expect-error Children are settable, thus this value can't be set in the constructor, maybe it should be updated whenever the children are updated - public get resourceUri(): vscode.Uri { - return vscode.Uri.file(this.path) - } -} - -export abstract class CoverageNode extends BaseNode { - constructor( - path: string, - label: string, - children: CoverageNode[], - collapsibleState: vscode.TreeItemCollapsibleState | undefined, - private readonly coverageLevelThresholds: CoverageLevelThresholds - ) { - super(path, label, children, collapsibleState) - } - - private getCoveragePercent(): number { - return this.totalLinesCount === 0 ? 100 : (this.coveredLinesCount / this.totalLinesCount) * 100 - } - - abstract get totalLinesCount(): number - - abstract get coveredLinesCount(): number - - // @ts-expect-error Children are settable, thus this value can't be set in the constructor, maybe it should be updated whenever the children are updated - get tooltip(): string { - return `${this.label}: ${this.formatCoverage()}` - } - - // @ts-expect-error Children are settable, thus this value can't be set in the constructor, maybe it should be updated whenever the children are updated - get description(): string { - return this.formatCoverage() - } - - private formatCoverage(): string { - return `${+this.getCoveragePercent().toFixed(1)}%` - } - - private getCoverageLevel(): CoverageLevel { - const coverageLevel = - this.getCoveragePercent() >= this.coverageLevelThresholds.sufficientCoverageThreshold - ? CoverageLevel.High - : this.getCoveragePercent() >= this.coverageLevelThresholds.lowCoverageThreshold - ? CoverageLevel.Medium - : CoverageLevel.Low - return coverageLevel - } - - // @ts-expect-error Children are settable, thus this value can't be set in the constructor, maybe it should be updated whenever the children are updated - get iconPath(): { light: string; dark: string } { - const light = iopath.join(__dirname, "..", "resources", "light", `${this.getCoverageLevel().toString()}.svg`) - const dark = iopath.join(__dirname, "..", "resources", "dark", `${this.getCoverageLevel().toString()}.svg`) - return { - light, - dark - } - } -} - -class RootCoverageNode extends BaseNode { - constructor(path: string, label: string, children: CoverageNode[] = []) { - super(path, label, children, vscode.TreeItemCollapsibleState.Collapsed) - } - - get totalLinesCount(): number { - let sum = 0 - this.children.forEach((n) => (sum += n.totalLinesCount)) - return sum - } - - get coveredLinesCount(): number { - let sum = 0 - this.children.forEach((n) => (sum += n.coveredLinesCount)) - return sum - } + private readonly _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter() + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event } -class FolderCoverageNode extends CoverageNode { - constructor(path: string, label: string, children: CoverageNode[] = [], coverageLevelThresholds: CoverageLevelThresholds) { - super(path, label, children, vscode.TreeItemCollapsibleState.Collapsed, coverageLevelThresholds) - } - get totalLinesCount(): number { - let sum = 0 - this.children.forEach((n) => (sum += n.totalLinesCount)) - return sum - } - - get coveredLinesCount(): number { - let sum = 0 - this.children.forEach((n) => (sum += n.coveredLinesCount)) - return sum - } -} - -class FileCoverageNode extends CoverageNode { - constructor( - path: string, - label: string, - coverageLevelThresholds: CoverageLevelThresholds, - public readonly totalLinesCount: number, - public readonly coveredLinesCount: number - ) { - super(path, label, [], vscode.TreeItemCollapsibleState.None, coverageLevelThresholds) - this.contextValue = FileCoverageNode.name - this.command = { - command: "vscode.open", - title: "Open", - arguments: [vscode.Uri.file(this.path)] - } - } -} diff --git a/src/Extension.ts b/src/Extension.ts index bf7c78b..53d0bc3 100644 --- a/src/Extension.ts +++ b/src/Extension.ts @@ -1,7 +1,8 @@ // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below import * as vscode from "vscode" -import { FileCoverageDataProvider, type CoverageNode } from "./DataProvider" +import { type CoverageNode } from "./TreeNodes"; +import { FileCoverageDataProvider } from "./DataProvider" import { CoverageParser } from "./CoverageParser" import { FilesLoader } from "./FilesLoader" import { ConfigStore } from "./ConfigStore" @@ -14,7 +15,7 @@ export function activate(context: vscode.ExtensionContext): void { const logger = vscodeLogging.getExtensionLogger({ extName: "Koverage", level: "debug", // See LogLevel type in @vscode-logging/types for possible logLevels - logPath: context.logPath, // The logPath is only available from the `vscode.ExtensionContext` + logPath: context.logUri.fsPath, // The logPath is only available from the `vscode.ExtensionContext` logOutputChannel: outputChannel, // OutputChannel for the logger sourceLocationTracking: false }) @@ -34,7 +35,7 @@ export function activate(context: vscode.ExtensionContext): void { // --- Commands const refresh = vscode.commands.registerCommand("koverage.refresh", () => { - fileCoverageDataProvider.refresh("") + fileCoverageDataProvider.forceRefresh("") }) const generateCoverage = vscode.commands.registerCommand("koverage.generate", async () => await fileCoverageDataProvider.generateCoverage()) diff --git a/src/TreeNodes.ts b/src/TreeNodes.ts new file mode 100644 index 0000000..efbda9d --- /dev/null +++ b/src/TreeNodes.ts @@ -0,0 +1,127 @@ +import * as iopath from "path"; +import * as vscode from "vscode"; +import { CoverageLevel } from "./CoverageLevel"; +import { type CoverageLevelThresholds } from "./CoverageLevel"; + +export abstract class BaseNode extends vscode.TreeItem { + constructor( + public readonly path: string, + public readonly label: string, + public readonly children: CoverageNode[], + collapsibleState: vscode.TreeItemCollapsibleState | undefined + ) { + super(label, collapsibleState); + } + + // @ts-expect-error Children are settable, thus this value can't be set in the constructor, maybe it should be updated whenever the children are updated + public get resourceUri(): vscode.Uri { + return vscode.Uri.file(this.path); + } +} + +export class RootCoverageNode extends BaseNode { + constructor(path: string, label: string, children: CoverageNode[] = []) { + super(path, label, children, vscode.TreeItemCollapsibleState.Collapsed); + } + + get totalLinesCount(): number { + let sum = 0; + this.children.forEach((n) => (sum += n.totalLinesCount)); + return sum; + } + + get coveredLinesCount(): number { + let sum = 0; + this.children.forEach((n) => (sum += n.coveredLinesCount)); + return sum; + } +} + +export abstract class CoverageNode extends BaseNode { + constructor( + path: string, + label: string, + children: CoverageNode[], + collapsibleState: vscode.TreeItemCollapsibleState | undefined, + private readonly coverageLevelThresholds: CoverageLevelThresholds + ) { + super(path, label, children, collapsibleState); + } + + private getCoveragePercent(): number { + return this.totalLinesCount === 0 ? 100 : (this.coveredLinesCount / this.totalLinesCount) * 100; + } + + abstract get totalLinesCount(): number; + + abstract get coveredLinesCount(): number; + + // @ts-expect-error Children are settable, thus this value can't be set in the constructor, maybe it should be updated whenever the children are updated + get tooltip(): string { + return `${this.label}: ${this.formatCoverage()}`; + } + + // @ts-expect-error Children are settable, thus this value can't be set in the constructor, maybe it should be updated whenever the children are updated + get description(): string { + return this.formatCoverage(); + } + + private formatCoverage(): string { + return `${+this.getCoveragePercent().toFixed(1)}%`; + } + + private getCoverageLevel(): CoverageLevel { + const coverageLevel = this.getCoveragePercent() >= this.coverageLevelThresholds.sufficientCoverageThreshold + ? CoverageLevel.High + : this.getCoveragePercent() >= this.coverageLevelThresholds.lowCoverageThreshold + ? CoverageLevel.Medium + : CoverageLevel.Low; + return coverageLevel; + } + + // @ts-expect-error Children are settable, thus this value can't be set in the constructor, maybe it should be updated whenever the children are updated + get iconPath(): { light: string; dark: string; } { + const light = iopath.join(__dirname, "..", "resources", "light", `${this.getCoverageLevel().toString()}.svg`); + const dark = iopath.join(__dirname, "..", "resources", "dark", `${this.getCoverageLevel().toString()}.svg`); + return { + light, + dark + }; + } +} + +export class FileCoverageNode extends CoverageNode { + constructor( + path: string, + label: string, + coverageLevelThresholds: CoverageLevelThresholds, + public readonly totalLinesCount: number, + public readonly coveredLinesCount: number + ) { + super(path, label, [], vscode.TreeItemCollapsibleState.None, coverageLevelThresholds); + this.contextValue = FileCoverageNode.name; + this.command = { + command: "vscode.open", + title: "Open", + arguments: [vscode.Uri.file(this.path)] + }; + } +} + +export class FolderCoverageNode extends CoverageNode { + constructor(path: string, label: string, children: CoverageNode[] = [], coverageLevelThresholds: CoverageLevelThresholds) { + super(path, label, children, vscode.TreeItemCollapsibleState.Collapsed, coverageLevelThresholds); + } + + get totalLinesCount(): number { + let sum = 0; + this.children.forEach((n) => (sum += n.totalLinesCount)); + return sum; + } + + get coveredLinesCount(): number { + let sum = 0; + this.children.forEach((n) => (sum += n.coveredLinesCount)); + return sum; + } +} diff --git a/webpack.config.js b/webpack.config.js index 0d575bc..d73c52c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,13 +12,14 @@ const config = { __filename: false, __dirname: false, }, - entry: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ + entry: "./src/Extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ path: path.resolve(__dirname, "dist"), filename: "extension.js", libraryTarget: "commonjs2", devtoolModuleFilenameTemplate: "../[resource-path]", + clean: true, }, devtool: "source-map", externals: { @@ -34,15 +35,7 @@ const config = { test: /\.ts$/, exclude: /(node_modules|bower_components)/, use: { - loader: "swc-loader", - options: { - "sourceMap": true, - jsc: { - parser: { - syntax: "typescript", - }, - }, - }, + loader: "ts-loader" }, }, ], diff --git a/yarn.lock b/yarn.lock index 9a6fe8d..f37daaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1175,7 +1175,7 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^5.15.0: +enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: version "5.15.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== @@ -2355,7 +2355,7 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^4.0.4: +micromatch@^4.0.0, micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -2955,7 +2955,7 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2: +semver@^7.0.0, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -3072,6 +3072,11 @@ source-map@^0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + stack-generator@^2.0.5: version "2.0.10" resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d" @@ -3299,6 +3304,17 @@ triple-beam@^1.2.0, triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== +ts-loader@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.0.tgz#f0a51dda37cc4d8e43e6cb14edebbc599b0c3aa2" + integrity sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + source-map "^0.7.4" + tsconfig-paths@^3.14.2: version "3.14.2" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088"