From f3ce862e3bf579eaa332531b4f4a99bfbe791363 Mon Sep 17 00:00:00 2001 From: Masahiro Hiramori Date: Tue, 13 Jan 2026 01:44:14 +0900 Subject: [PATCH 1/4] refactor: migrate logging from custom Logger to LogTape --- package-lock.json | 10 ++ package.json | 3 +- src/commands/ModuleInstantiation.ts | 13 +- src/ctags.ts | 41 +++--- src/extension.ts | 47 +++--- src/extensionManager.ts | 21 ++- src/fliplot/FliplotCustomEditor.ts | 10 +- src/fliplot/FliplotPanel.ts | 16 +-- src/languageServer/index.ts | 5 +- src/languageServer/manager.ts | 13 +- src/linter/BaseLinter.ts | 8 +- src/linter/IcarusLinter.ts | 13 +- src/linter/LintManager.ts | 42 +++--- src/linter/ModelsimLinter.ts | 9 +- src/linter/SlangLinter.ts | 19 ++- src/linter/VeribleVerilogLintLinter.ts | 18 +-- src/linter/VerilatorLinter.ts | 19 ++- src/linter/XvlogLinter.ts | 12 +- src/logger.ts | 110 -------------- src/logging.ts | 74 ++++++++++ src/logtape-vscode-sink.ts | 114 +++++++++++++++ src/providers/CompletionItemProvider.ts | 12 +- src/providers/DefinitionProvider.ts | 12 +- src/providers/DocumentSymbolProvider.ts | 12 +- src/providers/FormatProvider.ts | 23 ++- src/providers/HoverProvider.ts | 14 +- src/test/ctags.test.ts | 19 +-- src/test/definitionProvider.test.ts | 10 +- src/test/documentSymbolProvider.test.ts | 16 +-- src/test/format.test.ts | 3 +- src/test/hover.test.ts | 3 +- src/test/iverilog.test.ts | 5 +- src/test/languageServer.test.ts | 4 +- src/test/logTestUtils.ts | 181 ++++++++++++++++++++++++ src/test/logger.test.ts | 121 ---------------- src/test/logtape.test.ts | 88 ++++++++++++ src/test/moduleInstantiation.test.ts | 4 - src/test/verible-verilog-lint.test.ts | 4 +- src/test/verilator.test.ts | 14 +- tsconfig.json | 2 +- 40 files changed, 654 insertions(+), 510 deletions(-) delete mode 100644 src/logger.ts create mode 100644 src/logging.ts create mode 100644 src/logtape-vscode-sink.ts create mode 100644 src/test/logTestUtils.ts delete mode 100644 src/test/logger.test.ts create mode 100644 src/test/logtape.test.ts diff --git a/package-lock.json b/package-lock.json index 1b43107b..330e970f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "veriloghdl", "version": "1.22.2", "dependencies": { + "@logtape/logtape": "^1.3.6", "@vscode/test-cli": "^0.0.12", "js-yaml": "^4.1.1", "semver": "^7.7.3", @@ -873,6 +874,15 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@logtape/logtape": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@logtape/logtape/-/logtape-1.3.6.tgz", + "integrity": "sha512-OaK8eal8zcjB0GZbllXKgUC2T9h/GyNLQyQXjJkf1yum7SZKTWs9gs/t8NMS0kVVaSnA7bhU0Sjws/Iy4e0/IQ==", + "funding": [ + "https://github.com/sponsors/dahlia" + ], + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", diff --git a/package.json b/package.json index fdf20262..ff5c1c46 100644 --- a/package.json +++ b/package.json @@ -682,13 +682,14 @@ "syntax": "js-yaml ./syntaxes/systemverilog.tmLanguage.yaml >./syntaxes/systemverilog.tmLanguage.json" }, "dependencies": { - "@vscode/test-cli": "^0.0.12", + "@logtape/logtape": "^1.3.6", "js-yaml": "^4.1.1", "semver": "^7.7.3", "vscode-languageclient": "^9.0.1", "which": "^6.0.0" }, "devDependencies": { + "@vscode/test-cli": "^0.0.12", "@types/node": "~25.0.3", "@types/semver": "^7.7.1", "@types/vscode": "^1.107.0", diff --git a/src/commands/ModuleInstantiation.ts b/src/commands/ModuleInstantiation.ts index a8b60f50..bf13bea4 100644 --- a/src/commands/ModuleInstantiation.ts +++ b/src/commands/ModuleInstantiation.ts @@ -3,7 +3,9 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; import { Ctags, Symbol } from '../ctags'; -import { logger } from '../extension'; +import { getExtensionLogger } from '../logging'; + +const logger = () => getExtensionLogger('Command', 'ModuleInstantiation'); export function instantiateModuleInteract() { if (!isCtagsEnabled()) { @@ -41,8 +43,9 @@ export async function instantiateModule(srcpath: string): Promise tag.name); - logger.info(`Module name: ${ module.name}`); + log.info`Module name: ${module.name}`; let paramString = ``; if (parametersName.length > 0) { paramString = `\n#(\n${instantiatePort(parametersName)})\n`; } - logger.info(`portsName: ${ portsName.toString()}`); + log.info`portsName: ${portsName.toString()}`; return new vscode.SnippetString() .appendText(`${module.name } `) .appendText(paramString) diff --git a/src/ctags.ts b/src/ctags.ts index 9f5ad9e4..2fdbb5f8 100644 --- a/src/ctags.ts +++ b/src/ctags.ts @@ -2,7 +2,8 @@ import * as vscode from 'vscode'; import {exec as execNonPromise} from 'child_process'; import * as util from 'util'; -import { Logger } from './logger'; +import { type Logger } from '@logtape/logtape'; +import { getExtensionLogger } from './logging'; import { END_OF_LINE } from './constants'; const exec = util.promisify(execNonPromise); @@ -237,21 +238,18 @@ export class Ctags { */ async execCtags(filepath: string): Promise { const command: string = `${this.ctagBinPath } -f - --fields=+K --sort=no --excmd=n --fields-SystemVerilog=+{parameter} "${ filepath }"`; - this.logger.info(`Executing Command: ${ command}`); + this.logger.info`Executing Command: ${command}`; try { const {stdout, stderr} = await exec(command); if(stdout) { return stdout.toString(); } if(stderr) { - this.logger.error(`stderr> ${ stderr}`); + this.logger.error`stderr> ${stderr}`; } } catch (err) { - this.logger.error(`Exception caught: ${ err instanceof Error ? err.message : String(err)}`); - if (err instanceof Error && err.stack) { - this.logger.error(err.stack); - } + this.logger.error`Exception caught: ${err}`; } // Return empty promise if ctags path is not set to avoid errors when indexing @@ -287,8 +285,8 @@ export class Ctags { const lineNo = Number(lineNoStr.slice(0, -2)) - 1; return new Symbol(name, type, pattern, lineNo, parentScope, parentType, lineNo, false); } catch (err) { - this.logger.error(`Line Parser: ${ err}`); - this.logger.error(`Line: ${ line}`); + this.logger.error`Line Parser: ${err}`; + this.logger.error`Line: ${line}`; } return undefined; } @@ -301,9 +299,9 @@ export class Ctags { async buildSymbolsList(tags: string): Promise { try { if (this.isDirty) { - this.logger.info('building symbols'); + this.logger.info`building symbols`; if (tags === '') { - this.logger.error('No output from ctags'); + this.logger.error`No output from ctags`; return; } // Parse ctags output @@ -353,10 +351,7 @@ export class Ctags { this.isDirty = false; } } catch (err) { - this.logger.error(String(err)); - if (err instanceof Error && err.stack) { - this.logger.error(err.stack); - } + this.logger.error`${err}`; } } @@ -364,7 +359,7 @@ export class Ctags { * Indexes the document by running ctags and building the symbols list. */ async index(): Promise { - this.logger.info('indexing ', this.doc.uri.fsPath); + this.logger.info`indexing ${this.doc.uri.fsPath}`; const output = await this.execCtags(this.doc.uri.fsPath); await this.buildSymbolsList(output); @@ -377,16 +372,14 @@ export class Ctags { */ export class CtagsManager { private filemap: Map = new Map(); - private logger!: Logger; + private readonly logger = getExtensionLogger('Ctags', 'Manager'); private enabled = false; /** - * Configures the CtagsManager with a logger and sets up event listeners. - * @param logger - The logger instance for output + * Configures the CtagsManager and sets up event listeners. */ - configure(logger: Logger) { - this.logger = logger; - this.logger.info('ctags manager configure'); + configure() { + this.logger.info`ctags manager configure`; this.updateConfig(); vscode.workspace.onDidSaveTextDocument(this.onSave.bind(this)); vscode.workspace.onDidCloseTextDocument(this.onClose.bind(this)); @@ -402,7 +395,7 @@ export class CtagsManager { const nextEnabled = config.get('enabled', false); if (this.enabled !== nextEnabled) { this.enabled = nextEnabled; - this.logger.info(`ctags enabled: ${ this.enabled}`); + this.logger.info`ctags enabled: ${this.enabled}`; this.invalidateCache(); } } @@ -440,7 +433,7 @@ export class CtagsManager { * @param doc - The document that was saved */ onSave(doc: vscode.TextDocument) { - this.logger.info('on save'); + this.logger.info`on save`; if (!this.enabled) { return; } diff --git a/src/extension.ts b/src/extension.ts index 93376e94..6367dbfd 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -11,31 +11,31 @@ import * as ModuleInstantiation from './commands/ModuleInstantiation'; import * as FormatProvider from './providers/FormatProvider'; import { ExtensionManager } from './extensionManager'; import { initAllLanguageClients, stopAllLanguageClients } from './languageServer'; -import { createLogger, Logger } from './logger'; +import { bootstrapLogging, disposeLogging, getExtensionLogger } from './logging'; import { FliplotPanel } from './fliplot/FliplotPanel'; import { FliplotCustomEditor } from './fliplot/FliplotCustomEditor'; -export let logger: Logger; // Global logger -const ctagsManager = new CtagsManager(); +let ctagsManager: CtagsManager | undefined; const extensionID: string = 'mshr-h.veriloghdl'; let lintManager: LintManager; -export function activate(context: vscode.ExtensionContext) { - logger = createLogger('Verilog'); - logger.info(`${extensionID } is now active.`); +export async function activate(context: vscode.ExtensionContext) { + await bootstrapLogging(); - const extMgr = new ExtensionManager(context, extensionID, logger.getChild('ExtensionManager')); + const logger = getExtensionLogger(); + logger.info("Extension activating", { extensionId: extensionID }); + + const extMgr = new ExtensionManager(context, extensionID); if (extMgr.isVersionUpdated()) { extMgr.showChangelogNotification(); } - // Configure ctags - ctagsManager.configure(logger); + ctagsManager = new CtagsManager(); + ctagsManager.configure(); // Configure Document Symbol Provider const verilogDocumentSymbolProvider = new DocumentSymbolProvider.VerilogDocumentSymbolProvider( - logger.getChild('VerilogDocumentSymbolProvider'), ctagsManager, ); context.subscriptions.push( @@ -54,7 +54,6 @@ export function activate(context: vscode.ExtensionContext) { // Configure Completion Item Provider // Trigger on ".", "(", "=" const verilogCompletionItemProvider = new CompletionItemProvider.VerilogCompletionItemProvider( - logger.getChild('VerilogCompletionItemProvider'), ctagsManager, ); context.subscriptions.push( @@ -78,7 +77,6 @@ export function activate(context: vscode.ExtensionContext) { // Configure Hover Providers const verilogHoverProvider = new HoverProvider.VerilogHoverProvider( - logger.getChild('VerilogHoverProvider'), ctagsManager, ); context.subscriptions.push( @@ -96,7 +94,6 @@ export function activate(context: vscode.ExtensionContext) { // Configure Definition Providers const verilogDefinitionProvider = new DefinitionProvider.VerilogDefinitionProvider( - logger.getChild('VerilogDefinitionProvider'), ctagsManager ); context.subscriptions.push( @@ -113,18 +110,14 @@ export function activate(context: vscode.ExtensionContext) { ); // Configure Format Provider - const verilogFormatProvider = new FormatProvider.VerilogFormatProvider( - logger.getChild('VerilogFormatProvider') - ); + const verilogFormatProvider = new FormatProvider.VerilogFormatProvider(); context.subscriptions.push( vscode.languages.registerDocumentFormattingEditProvider( { scheme: 'file', language: 'verilog' }, verilogFormatProvider ) ); - const systemVerilogFormatProvider = new FormatProvider.SystemVerilogFormatProvider( - logger.getChild('SystemVerilogFormatProvider') - ); + const systemVerilogFormatProvider = new FormatProvider.SystemVerilogFormatProvider(); context.subscriptions.push( vscode.languages.registerDocumentFormattingEditProvider( { scheme: 'file', language: 'systemverilog' }, @@ -141,21 +134,21 @@ export function activate(context: vscode.ExtensionContext) { ); // Register command for manual linting - lintManager = new LintManager(logger.getChild('LintManager')); + lintManager = new LintManager(); context.subscriptions.push( vscode.commands.registerCommand('verilog.lint', lintManager.runLintTool, lintManager) ); context.subscriptions.push( vscode.commands.registerCommand('verilog.openFliplot', () => { - FliplotPanel.show(context, logger.getChild('Fliplot')); + FliplotPanel.show(context); }) ); context.subscriptions.push( vscode.window.registerCustomEditorProvider( FliplotCustomEditor.viewType, - new FliplotCustomEditor(context, logger.getChild('Fliplot')), + new FliplotCustomEditor(context), { webviewOptions: { retainContextWhenHidden: true }, } @@ -168,15 +161,17 @@ export function activate(context: vscode.ExtensionContext) { return; } stopAllLanguageClients().finally(() => { - initAllLanguageClients(logger); + initAllLanguageClients(); }); }); - initAllLanguageClients(logger); + initAllLanguageClients(); - logger.info(`${extensionID } activation finished.`); + logger.info("Extension activated", { extensionId: extensionID }); } export async function deactivate(): Promise { - logger.info('Deactivated'); + const logger = getExtensionLogger(); + logger.info("Extension deactivating"); await stopAllLanguageClients(); + await disposeLogging(); } diff --git a/src/extensionManager.ts b/src/extensionManager.ts index 4e017492..eed7435e 100644 --- a/src/extensionManager.ts +++ b/src/extensionManager.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT import * as vscode from 'vscode'; import { SemVer } from 'semver'; -import { Logger } from './logger'; +import { getExtensionLogger } from './logging'; interface PackageJSON { version: string; @@ -13,15 +13,16 @@ export class ExtensionManager { private extensionID: string; private packageJSON: PackageJSON; private extensionPath: string; - private logger: Logger; + private readonly logger = getExtensionLogger('Core', 'ExtensionManager'); - constructor(context: vscode.ExtensionContext, extensionID: string, logger: Logger) { + constructor(context: vscode.ExtensionContext, extensionID: string) { this.context = context; this.extensionID = extensionID; - this.logger = logger; const extension = vscode.extensions.getExtension(this.extensionID); if (!extension) { - throw new Error(`Extension ${extensionID} not found`); + const errorMessage = `Extension ${extensionID} not found`; + this.logger.fatal("Extension not found", { extensionId: extensionID }); + throw new Error(errorMessage); } this.packageJSON = extension.packageJSON; this.extensionPath = extension.extensionPath; @@ -33,12 +34,10 @@ export class ExtensionManager { // update version value this.context.globalState.update('version', currentVersion.version); - this.logger.info( - `previousVersion: ${ - JSON.stringify(previousVersion.version) - }, currentVersion: ${ - JSON.stringify(currentVersion.version)}` - ); + this.logger.info("Version check", { + previousVersion: previousVersion.version, + currentVersion: currentVersion.version, + }); return previousVersion < currentVersion; } diff --git a/src/fliplot/FliplotCustomEditor.ts b/src/fliplot/FliplotCustomEditor.ts index 59e764e8..b3e422e6 100644 --- a/src/fliplot/FliplotCustomEditor.ts +++ b/src/fliplot/FliplotCustomEditor.ts @@ -1,19 +1,17 @@ // SPDX-License-Identifier: MIT import * as path from 'path'; import * as vscode from 'vscode'; - -import { Logger } from '../logger'; +import { getExtensionLogger } from '../logging'; import { FliplotPanel } from './FliplotPanel'; export class FliplotCustomEditor implements vscode.CustomTextEditorProvider { static readonly viewType = 'verilog.fliplotEditor'; private readonly context: vscode.ExtensionContext; - private readonly logger: Logger; + private readonly logger = getExtensionLogger('Fliplot', 'CustomEditor'); - constructor(context: vscode.ExtensionContext, logger: Logger) { + constructor(context: vscode.ExtensionContext) { this.context = context; - this.logger = logger; } async resolveCustomTextEditor( @@ -68,6 +66,6 @@ export class FliplotCustomEditor implements vscode.CustomTextEditorProvider { changeDisposable.dispose(); }); - this.logger.info(`Fliplot custom editor opened: ${document.uri.fsPath}`); + this.logger.info("Custom editor opened", { path: document.uri.fsPath }); } } diff --git a/src/fliplot/FliplotPanel.ts b/src/fliplot/FliplotPanel.ts index e680cb31..9ce7e937 100644 --- a/src/fliplot/FliplotPanel.ts +++ b/src/fliplot/FliplotPanel.ts @@ -1,19 +1,18 @@ // SPDX-License-Identifier: MIT import * as vscode from 'vscode'; - -import { Logger } from '../logger'; +import { getExtensionLogger } from '../logging'; export class FliplotPanel { private static currentPanel: FliplotPanel | undefined; private readonly panel: vscode.WebviewPanel; private readonly extensionUri: vscode.Uri; - private readonly logger: Logger; + private readonly logger = getExtensionLogger('Fliplot', 'Panel'); private readonly disposables: vscode.Disposable[] = []; private isReady = false; private pendingVcd: { content: string; title?: string } | null = null; - static show(context: vscode.ExtensionContext, logger: Logger): FliplotPanel { + static show(context: vscode.ExtensionContext): FliplotPanel { const column = vscode.window.activeTextEditor?.viewColumn ?? vscode.ViewColumn.One; if (FliplotPanel.currentPanel) { FliplotPanel.currentPanel.panel.reveal(column); @@ -30,14 +29,13 @@ export class FliplotPanel { } ); - FliplotPanel.currentPanel = new FliplotPanel(panel, context.extensionUri, logger); + FliplotPanel.currentPanel = new FliplotPanel(panel, context.extensionUri); return FliplotPanel.currentPanel; } - private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri, logger: Logger) { + private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) { this.panel = panel; this.extensionUri = extensionUri; - this.logger = logger; this.panel.webview.html = FliplotPanel.buildHtml(this.panel.webview, this.extensionUri); this.panel.onDidDispose(() => this.dispose(), null, this.disposables); @@ -55,12 +53,12 @@ export class FliplotPanel { null, this.disposables ); - this.logger.info('Fliplot panel opened.'); + this.logger.info("Fliplot panel opened"); } dispose() { FliplotPanel.currentPanel = undefined; - this.logger.info('Fliplot panel disposed.'); + this.logger.info("Fliplot panel disposed"); while (this.disposables.length) { const disposable = this.disposables.pop(); if (disposable) { diff --git a/src/languageServer/index.ts b/src/languageServer/index.ts index 0299fc8a..17fae34a 100644 --- a/src/languageServer/index.ts +++ b/src/languageServer/index.ts @@ -1,13 +1,12 @@ // SPDX-License-Identifier: MIT -import { Logger } from '../logger'; import { createLanguageServerDefinitions } from './definitions'; import { LanguageServerManager } from './manager'; let manager: LanguageServerManager | undefined; -export function initAllLanguageClients(logger: Logger) { +export function initAllLanguageClients() { if (!manager) { - manager = new LanguageServerManager(logger, createLanguageServerDefinitions()); + manager = new LanguageServerManager(createLanguageServerDefinitions()); } manager.initAll(); } diff --git a/src/languageServer/manager.ts b/src/languageServer/manager.ts index efcd1b85..576aa758 100644 --- a/src/languageServer/manager.ts +++ b/src/languageServer/manager.ts @@ -1,17 +1,14 @@ // SPDX-License-Identifier: MIT import * as vscode from 'vscode'; import { LanguageClient, ServerOptions } from 'vscode-languageclient/node'; - -import { Logger } from '../logger'; +import { getExtensionLogger } from '../logging'; import { LanguageServerDefinition } from './definitions'; export class LanguageServerManager { private languageClients = new Map(); + private readonly logger = getExtensionLogger('LanguageServer', 'Manager'); - constructor( - private readonly logger: Logger, - private readonly definitions: LanguageServerDefinition[] - ) {} + constructor(private readonly definitions: LanguageServerDefinition[]) {} initAll() { this.languageClients.clear(); @@ -25,7 +22,7 @@ export class LanguageServerManager { for (const [name, client] of this.languageClients) { if (client.isRunning()) { stops.push(client.stop()); - this.logger.info(`"${ name }" language server stopped.`); + this.logger.info`"${name}" language server stopped.`; } } return Promise.all(stops); @@ -68,6 +65,6 @@ export class LanguageServerManager { } client.start(); - this.logger.info(`"${ definition.name }" language server started.`); + this.logger.info`"${definition.name}" language server started.`; } } diff --git a/src/linter/BaseLinter.ts b/src/linter/BaseLinter.ts index 88f95e6a..de88b8a8 100644 --- a/src/linter/BaseLinter.ts +++ b/src/linter/BaseLinter.ts @@ -2,7 +2,8 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as child from 'child_process'; -import { Logger } from '../logger'; +import { type Logger } from '@logtape/logtape'; +import { getExtensionLogger } from '../logging'; /** Common configuration interface for linters */ export interface LinterConfig { @@ -40,12 +41,11 @@ export default abstract class BaseLinter { * Creates a new BaseLinter instance. * @param name - The name of the linter * @param diagnosticCollection - The VS Code diagnostic collection - * @param logger - The logger instance */ - constructor(name: string, diagnosticCollection: vscode.DiagnosticCollection, logger: Logger) { + constructor(name: string, diagnosticCollection: vscode.DiagnosticCollection) { this.diagnosticCollection = diagnosticCollection; this.name = name; - this.logger = logger; + this.logger = getExtensionLogger('Linter', name); // Register configuration change listener vscode.workspace.onDidChangeConfiguration(() => { diff --git a/src/linter/IcarusLinter.ts b/src/linter/IcarusLinter.ts index 631d64ad..b5b3c188 100644 --- a/src/linter/IcarusLinter.ts +++ b/src/linter/IcarusLinter.ts @@ -3,7 +3,6 @@ import * as vscode from 'vscode'; import * as child from 'child_process'; import * as path from 'path'; import BaseLinter from './BaseLinter'; -import { Logger } from '../logger'; const standardToArg: Map = new Map([ ['Verilog-95', '-g1995'], @@ -18,8 +17,8 @@ export default class IcarusLinter extends BaseLinter { private configuration!: vscode.WorkspaceConfiguration; private standards!: Map; - constructor(diagnosticCollection: vscode.DiagnosticCollection, logger: Logger) { - super('iverilog', diagnosticCollection, logger); + constructor(diagnosticCollection: vscode.DiagnosticCollection) { + super('iverilog', diagnosticCollection); this.updateConfig(); } @@ -53,10 +52,10 @@ export default class IcarusLinter extends BaseLinter { } protected lint(doc: vscode.TextDocument) { - this.logger.info('Executing IcarusLinter.lint()'); + this.logger.info`Executing IcarusLinter.lint()`; const binPath: string = path.join(this.config.linterInstalledPath, 'iverilog'); - this.logger.info(`iverilog binary path: ${ binPath}`); + this.logger.info`iverilog binary path: ${binPath}`; let args: string[] = []; args.push('-t null'); @@ -73,9 +72,7 @@ export default class IcarusLinter extends BaseLinter { const command: string = `${binPath } ${ args.join(' ')}`; const cwd: string = this.getWorkingDirectory(doc); - this.logger.info('Execute'); - this.logger.info(' command: ', command); - this.logger.info(' cwd : ', cwd); + this.logger.info("Executing", { command, cwd }); const _: child.ChildProcess = child.exec( command, diff --git a/src/linter/LintManager.ts b/src/linter/LintManager.ts index a7536465..f827a16e 100644 --- a/src/linter/LintManager.ts +++ b/src/linter/LintManager.ts @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT import * as vscode from 'vscode'; +import { getExtensionLogger } from '../logging'; import BaseLinter from './BaseLinter'; import IcarusLinter from './IcarusLinter'; import ModelsimLinter from './ModelsimLinter'; @@ -7,20 +8,18 @@ import VerilatorLinter from './VerilatorLinter'; import SlangLinter from './SlangLinter'; import XvlogLinter from './XvlogLinter'; import VeribleVerilogLintLinter from './VeribleVerilogLintLinter'; -import { Logger } from '../logger'; export default class LintManager { private subscriptions: vscode.Disposable[]; private linter: BaseLinter | null; private diagnosticCollection: vscode.DiagnosticCollection; - private logger: Logger; + private readonly logger = getExtensionLogger('Linter', 'Manager'); - constructor(logger: Logger) { + constructor() { this.subscriptions = []; this.linter = null; this.diagnosticCollection = vscode.languages.createDiagnosticCollection(); - this.logger = logger; vscode.workspace.onDidOpenTextDocument(this.lint, this, this.subscriptions); vscode.workspace.onDidSaveTextDocument(this.lint, this, this.subscriptions); vscode.workspace.onDidCloseTextDocument(this.removeFileDiagnostics, this, this.subscriptions); @@ -36,26 +35,17 @@ export default class LintManager { getLinterFromString(name: string): BaseLinter | null { switch (name) { case 'iverilog': - return new IcarusLinter(this.diagnosticCollection, this.logger.getChild('IcarusLinter')); + return new IcarusLinter(this.diagnosticCollection); case 'xvlog': - return new XvlogLinter(this.diagnosticCollection, this.logger.getChild('XvlogLinter')); + return new XvlogLinter(this.diagnosticCollection); case 'modelsim': - return new ModelsimLinter( - this.diagnosticCollection, - this.logger.getChild('ModelsimLinter') - ); + return new ModelsimLinter(this.diagnosticCollection); case 'verilator': - return new VerilatorLinter( - this.diagnosticCollection, - this.logger.getChild('VerilatorLinter') - ); + return new VerilatorLinter(this.diagnosticCollection); case 'slang': - return new SlangLinter(this.diagnosticCollection, this.logger.getChild('SlangLinter')); + return new SlangLinter(this.diagnosticCollection); case 'verible-verilog-lint': - return new VeribleVerilogLintLinter( - this.diagnosticCollection, - this.logger.getChild('VeribleVerilogLintLinter') - ); + return new VeribleVerilogLintLinter(this.diagnosticCollection); default: return null; } @@ -71,17 +61,17 @@ export default class LintManager { } if (linterName === undefined) { - this.logger.warn('Linter name is undefined'); + this.logger.warn("Linter name is undefined"); return; } this.linter = this.getLinterFromString(linterName); if (this.linter === null) { - this.logger.warn(`Invalid linter name: ${ linterName}`); + this.logger.warn("Invalid linter name", { linter: linterName }); return; } - this.logger.info(`Using linter: ${ this.linter.name}`); + this.logger.info("Linter configured", { linter: this.linter.name }); } lint(doc: vscode.TextDocument) { @@ -107,7 +97,7 @@ export default class LintManager { async runLintTool() { // Check for language id - this.logger.info('Executing runLintTool()'); + this.logger.info("Manual lint tool execution started"); const editor = vscode.window.activeTextEditor; if ( !editor || @@ -151,7 +141,7 @@ export default class LintManager { } ); if (linterStr === undefined) { - this.logger.error('linterStr is undefined'); + this.logger.error("Linter selection cancelled"); return; } // Create and run the linter with progress bar @@ -163,10 +153,10 @@ export default class LintManager { async (_progress, _token) => { const linter: BaseLinter | null = this.getLinterFromString(linterStr.label); if (linter === null) { - this.logger.error(`Cannot find linter name: ${ linterStr.label}`); + this.logger.error("Linter not found", { linter: linterStr.label }); return; } - this.logger.info(`Using ${ linter.name } linter`); + this.logger.info("Running linter", { linter: linter.name }); linter.removeFileDiagnostics(editor.document); linter.startLint(editor.document); diff --git a/src/linter/ModelsimLinter.ts b/src/linter/ModelsimLinter.ts index ec8caccd..33f677e0 100644 --- a/src/linter/ModelsimLinter.ts +++ b/src/linter/ModelsimLinter.ts @@ -2,14 +2,13 @@ import * as vscode from 'vscode'; import * as child from 'child_process'; import BaseLinter from './BaseLinter'; -import { Logger } from '../logger'; import { END_OF_LINE } from '../constants'; export default class ModelsimLinter extends BaseLinter { private modelsimWork!: string; - constructor(diagnosticCollection: vscode.DiagnosticCollection, logger: Logger) { - super('modelsim', diagnosticCollection, logger); + constructor(diagnosticCollection: vscode.DiagnosticCollection) { + super('modelsim', diagnosticCollection); this.updateConfig(); } @@ -31,7 +30,7 @@ export default class ModelsimLinter extends BaseLinter { } protected lint(doc: vscode.TextDocument) { - this.logger.info('modelsim lint requested'); + this.logger.info`modelsim lint requested`; const cwd: string = this.getWorkingDirectory(doc); // no change needed for systemverilog const command: string = @@ -81,7 +80,7 @@ export default class ModelsimLinter extends BaseLinter { } } }); - this.logger.info(`${diagnostics.length } errors/warnings returned`); + this.logger.info`${diagnostics.length} errors/warnings returned`; this.diagnosticCollection.set(doc.uri, diagnostics); } ); diff --git a/src/linter/SlangLinter.ts b/src/linter/SlangLinter.ts index ac51226c..24ac6cdb 100644 --- a/src/linter/SlangLinter.ts +++ b/src/linter/SlangLinter.ts @@ -4,7 +4,6 @@ import * as child from 'child_process'; import * as path from 'path'; import * as process from 'process'; import BaseLinter from './BaseLinter'; -import { Logger } from '../logger'; import { END_OF_LINE } from '../constants'; const isWindows = process.platform === 'win32'; @@ -13,8 +12,8 @@ export default class SlangLinter extends BaseLinter { private configuration!: vscode.WorkspaceConfiguration; private useWSL!: boolean; - constructor(diagnosticCollection: vscode.DiagnosticCollection, logger: Logger) { - super('slang', diagnosticCollection, logger); + constructor(diagnosticCollection: vscode.DiagnosticCollection) { + super('slang', diagnosticCollection); this.updateConfig(); } @@ -44,10 +43,10 @@ export default class SlangLinter extends BaseLinter { if (isWindows) { if (this.useWSL) { docUri = this.convertToWslPath(docUri); - this.logger.info(`Rewrote docUri to ${docUri} for WSL`); + this.logger.info`Rewrote docUri to ${docUri} for WSL`; docFolder = this.convertToWslPath(docFolder); - this.logger.info(`Rewrote docFolder to ${docFolder} for WSL`); + this.logger.info`Rewrote docFolder to ${docFolder} for WSL`; } else { docUri = docUri.replace(/\\/g, '/'); docFolder = docFolder.replace(/\\/g, '/'); @@ -70,9 +69,7 @@ export default class SlangLinter extends BaseLinter { : docFolder : vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? docFolder; - this.logger.info('[slang] Execute'); - this.logger.info(`[slang] command: ${ command}`); - this.logger.info(`[slang] cwd : ${ cwd}`); + this.logger.info("Executing", { command, cwd }); const _: child.ChildProcess = child.exec( command, @@ -94,7 +91,7 @@ export default class SlangLinter extends BaseLinter { if (isWindows) { if (this.useWSL) { filePath = this.convertToWslPath(filePath); - this.logger.info(`Rewrote filePath to ${filePath} for WSL`); + this.logger.info`Rewrote filePath to ${filePath} for WSL`; } else { filePath = filePath.replace(/\\/g, '/'); } @@ -117,9 +114,9 @@ export default class SlangLinter extends BaseLinter { }); return; } - this.logger.warn(`[slang] failed to parse error: ${ line}`); + this.logger.warn`failed to parse error: ${line}`; }); - this.logger.info(`[slang] ${diagnostics.length} errors/warnings returned`); + this.logger.info`${diagnostics.length} errors/warnings returned`; this.diagnosticCollection.set(doc.uri, diagnostics); } ); diff --git a/src/linter/VeribleVerilogLintLinter.ts b/src/linter/VeribleVerilogLintLinter.ts index e828a4a6..1dd2d83c 100644 --- a/src/linter/VeribleVerilogLintLinter.ts +++ b/src/linter/VeribleVerilogLintLinter.ts @@ -4,14 +4,13 @@ import * as child from 'child_process'; import * as path from 'path'; import * as process from 'process'; import BaseLinter from './BaseLinter'; -import { Logger } from '../logger'; import { END_OF_LINE } from '../constants'; const isWindows = process.platform === 'win32'; export default class VeribleVerilogLintLinter extends BaseLinter { - constructor(diagnosticCollection: vscode.DiagnosticCollection, logger: Logger) { - super('verible-verilog-lint', diagnosticCollection, logger); + constructor(diagnosticCollection: vscode.DiagnosticCollection) { + super('verible-verilog-lint', diagnosticCollection); this.updateConfig(); } @@ -33,12 +32,11 @@ export default class VeribleVerilogLintLinter extends BaseLinter { } protected lint(doc: vscode.TextDocument) { - this.logger.info('Executing VeribleVerilogLintLinter.lint()'); + this.logger.info`Executing VeribleVerilogLintLinter.lint()`; const binName = isWindows ? 'verible-verilog-lint.exe' : 'verible-verilog-lint'; const binPath: string = path.join(this.config.linterInstalledPath, binName); - this.logger.info(`verible-verilog-lint binary path: ${ binPath}`); - + this.logger.info`verible-verilog-lint binary path: ${binPath}`; const docUri: string = doc.uri.fsPath; const cwd: string = this.getWorkingDirectory(doc); @@ -48,9 +46,7 @@ export default class VeribleVerilogLintLinter extends BaseLinter { const command: string = `${binPath } ${ args.join(' ')}`; - this.logger.info('[verible-verilog-lint] Execute'); - this.logger.info(`[verible-verilog-lint] command: ${ command}`); - this.logger.info(`[verible-verilog-lint] cwd : ${ cwd}`); + this.logger.info("Executing", { command, cwd }); const _: child.ChildProcess = child.exec( command, @@ -99,9 +95,7 @@ export default class VeribleVerilogLintLinter extends BaseLinter { }); }); - this.logger.info( - `[verible-verilog-lint] ${diagnostics.length} errors/warnings returned` - ); + this.logger.info`${diagnostics.length} errors/warnings returned`; this.diagnosticCollection.set(doc.uri, diagnostics); } ); diff --git a/src/linter/VerilatorLinter.ts b/src/linter/VerilatorLinter.ts index 93d58a7d..eb332e84 100644 --- a/src/linter/VerilatorLinter.ts +++ b/src/linter/VerilatorLinter.ts @@ -4,7 +4,6 @@ import * as child from 'child_process'; import * as path from 'path'; import * as process from 'process'; import BaseLinter from './BaseLinter'; -import { Logger } from '../logger'; import { END_OF_LINE } from '../constants'; const isWindows = process.platform === 'win32'; @@ -13,8 +12,8 @@ export default class VerilatorLinter extends BaseLinter { private configuration!: vscode.WorkspaceConfiguration; private useWSL!: boolean; - constructor(diagnosticCollection: vscode.DiagnosticCollection, logger: Logger) { - super('verilator', diagnosticCollection, logger); + constructor(diagnosticCollection: vscode.DiagnosticCollection) { + super('verilator', diagnosticCollection); this.updateConfig(); } @@ -85,9 +84,7 @@ export default class VerilatorLinter extends BaseLinter { args.push(`"${docUri}"`); const command: string = `${binPath } ${ args.join(' ')}`; - this.logger.info('[verilator] Execute'); - this.logger.info(`[verilator] command: ${ command}`); - this.logger.info(`[verilator] cwd : ${ cwd}`); + this.logger.info("Executing", { command, cwd }); const _: child.ChildProcess = child.exec( command, @@ -129,8 +126,8 @@ export default class VerilatorLinter extends BaseLinter { if (!line.startsWith('%')) { // allows for persistent - if (lastDiagMessageType === 'Warning') { this.logger.warn(line); } - else { this.logger.error(line); } + if (lastDiagMessageType === 'Warning') { this.logger.warn`${line}`; } + else { this.logger.error`${line}`; } return; } @@ -175,11 +172,11 @@ export default class VerilatorLinter extends BaseLinter { // stderr passthrough // probably better toggled with a parameter - if (rex.groups["severity"] === "Error") { this.logger.error(line); } - else if (rex.groups["severity"] === "Warning") { this.logger.warn(line); } + if (rex.groups["severity"] === "Error") { this.logger.error`${line}`; } + else if (rex.groups["severity"] === "Warning") { this.logger.warn`${line}`; } // theoretically, this shoudn't "fire", but just in case - else { this.logger.error(line); } + else { this.logger.error`${line}`; } // vscode problems are tied to files diff --git a/src/linter/XvlogLinter.ts b/src/linter/XvlogLinter.ts index 04a99091..819031e1 100644 --- a/src/linter/XvlogLinter.ts +++ b/src/linter/XvlogLinter.ts @@ -3,12 +3,11 @@ import * as vscode from 'vscode'; import {exec, ExecException} from 'child_process'; import * as path from 'path'; import BaseLinter from './BaseLinter'; -import { Logger } from '../logger'; import { END_OF_LINE } from '../constants'; export default class XvlogLinter extends BaseLinter { - constructor(diagnosticCollection: vscode.DiagnosticCollection, logger: Logger) { - super('xvlog', diagnosticCollection, logger); + constructor(diagnosticCollection: vscode.DiagnosticCollection) { + super('xvlog', diagnosticCollection); this.updateConfig(); } @@ -35,13 +34,12 @@ export default class XvlogLinter extends BaseLinter { args.push('-sv'); } args = args.concat(this.config.includePath.map((p: string) => `-i "${p}"`)); - this.logger.warn(this.config.includePath.join(' ')); + this.logger.warn`${this.config.includePath.join(' ')}`; args.push(this.config.arguments); args.push(`"${doc.fileName}"`); const command: string = `${binPath } ${ args.join(' ')}`; - this.logger.info('[xvlog] Execute'); - this.logger.info(`[xvlog] command: ${ command}`); + this.logger.info("Executing", { command }); exec(command, (_error: ExecException | null, stdout: string, _stderr: string) => { const diagnostics: vscode.Diagnostic[] = []; @@ -68,7 +66,7 @@ export default class XvlogLinter extends BaseLinter { diagnostics.push(diagnostic); }); - this.logger.info(`${diagnostics.length } errors/warnings returned`); + this.logger.info`${diagnostics.length} errors/warnings returned`; this.diagnosticCollection.set(doc.uri, diagnostics); }); } diff --git a/src/logger.ts b/src/logger.ts deleted file mode 100644 index cccc559f..00000000 --- a/src/logger.ts +++ /dev/null @@ -1,110 +0,0 @@ -import * as vscode from 'vscode'; - -/** - * A hierarchical logger that wraps VS Code's LogOutputChannel. - * Supports creating child loggers that prefix messages with their name. - * - * @example - * ```typescript - * const logger = createLogger('Verilog'); - * logger.info('Info message'); - * const childLogger = logger.getChild('ChildA'); - * childLogger.info('Message from child'); - * // Output: - * // Info message - * // [ChildA] Message from child - * ``` - */ -export class Logger { - private name: string; - private parentLogger: vscode.LogOutputChannel | Logger; - - /** - * Creates a new Logger instance. - * @param name - The name of this logger (used as prefix for child loggers) - * @param parentLogger - The parent LogOutputChannel or Logger - */ - constructor(name: string, parentLogger: vscode.LogOutputChannel | Logger) { - this.name = name; - this.parentLogger = parentLogger; - } - - /** - * Creates a child logger with a prefixed name. - * @param name - The name for the child logger - * @returns A new Logger instance that prefixes messages with [name] - */ - getChild(name: string) { - return new Logger(name, this); - } - - private log(level: 'trace' | 'info' | 'debug' | 'warn' | 'error', message: string, data?: unknown): void { - let formattedMessage = - this.parentLogger instanceof Logger ? `[${this.name}] ${message}` : `${message}`; - if (data) { - formattedMessage += JSON.stringify(data); - } - this.parentLogger[level](formattedMessage); - } - - /** - * Logs a trace-level message. - * @param message - The message to log - * @param data - Optional data to append as JSON - */ - trace(message: string, data?: unknown): void { - this.log('trace', message, data); - } - - /** - * Logs an info-level message. - * @param message - The message to log - * @param data - Optional data to append as JSON - */ - info(message: string, data?: unknown): void { - this.log('info', message, data); - } - - /** - * Logs a debug-level message. - * @param message - The message to log - * @param data - Optional data to append as JSON - */ - debug(message: string, data?: unknown): void { - this.log('debug', message, data); - } - - /** - * Logs a warning-level message. - * @param message - The message to log - * @param data - Optional data to append as JSON - */ - warn(message: string, data?: unknown): void { - this.log('warn', message, data); - } - - /** - * Logs an error-level message. - * @param message - The message to log - * @param data - Optional data to append as JSON - */ - error(message: string, data?: unknown): void { - this.log('error', message, data); - } - - /** - * Shows the output channel in the VS Code panel. - */ - show(): void { - this.parentLogger.show(); - } -} - -/** - * Creates a new root Logger with a VS Code LogOutputChannel. - * @param name - The name for the output channel - * @returns A new Logger instance - */ -export function createLogger(name: string): Logger { - return new Logger(name, vscode.window.createOutputChannel(name, { log: true })); -} diff --git a/src/logging.ts b/src/logging.ts new file mode 100644 index 00000000..b436a2b7 --- /dev/null +++ b/src/logging.ts @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +import { + configure, + getLogger as getLogTapeLogger, + reset, + type Logger, +} from '@logtape/logtape'; +import { createVSCodeSink, type VSCodeSink } from './logtape-vscode-sink'; + +export const ROOT_LOGGER_CATEGORY = 'Verilog'; + +let vscodeSink: VSCodeSink | undefined; +let configured = false; + +/** + * Initializes LogTape with the VS Code sink. Safe to call multiple times. + */ +export async function bootstrapLogging(): Promise { + if (configured) { + return; + } + vscodeSink = createVSCodeSink(ROOT_LOGGER_CATEGORY); + await configure({ + sinks: { vscode: vscodeSink }, + filters: {}, + loggers: [{ category: [ROOT_LOGGER_CATEGORY], level: 'debug', sinks: ['vscode'] }], + }); + configured = true; +} + +/** + * Disposes logging resources and resets LogTape configuration. + */ +export async function disposeLogging(): Promise { + await reset(); + vscodeSink?.dispose(); + vscodeSink = undefined; + configured = false; +} + +/** + * Returns a LogTape logger for the extension with the given category suffixes. + * + * @param category - Category suffixes to append to the root logger category. + * Use hierarchical categories for consistent organization. + * @returns A LogTape Logger instance for structured logging. + * + * @example + * // In a class (use hierarchical categories): + * class LintManager { + * private readonly logger = getExtensionLogger('Linter', 'Manager'); + * // Output: [Linter/Manager] message + * } + * + * @example + * // For providers: + * class VerilogHoverProvider { + * private readonly logger = getExtensionLogger('Provider', 'Hover'); + * // Output: [Provider/Hover] message + * } + * + * @example + * // In a top-level function (use lazy evaluation to avoid initialization issues): + * const logger = () => getExtensionLogger('Command', 'ModuleInstantiation'); + * // Output: [Command/ModuleInstantiation] message + * + * @example + * // Root logger (no category suffix): + * const logger = getExtensionLogger(); + * // Output: message (no prefix) + */ +export function getExtensionLogger(...category: string[]): Logger { + return getLogTapeLogger([ROOT_LOGGER_CATEGORY, ...category]); +} diff --git a/src/logtape-vscode-sink.ts b/src/logtape-vscode-sink.ts new file mode 100644 index 00000000..6c76ca48 --- /dev/null +++ b/src/logtape-vscode-sink.ts @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +import * as vscode from 'vscode'; +import type { LogRecord, Sink } from '@logtape/logtape'; + +/** + * A VS Code OutputChannel that can be used as a LogTape sink. + * Provides the show() method to reveal the output panel. + */ +export interface VSCodeSink extends Sink { + /** Reveals the output channel in the VS Code panel. */ + show(): void; + /** Disposes the output channel. */ + dispose(): void; +} + +/** + * Formats a LogTape log record's message parts into a single string. + * @param record - The log record to format + * @returns The formatted message string + */ +function formatMessage(record: LogRecord): string { + return record.message + .map((part) => (typeof part === 'string' ? part : JSON.stringify(part))) + .join(''); +} + +/** + * Formats structured properties from a log record. + * @param properties - The properties object from the log record + * @returns The formatted properties string, or empty string if no properties + */ +function formatProperties(properties: Record): string { + const keys = Object.keys(properties); + if (keys.length === 0) { + return ''; + } + const formatted = keys + .map((key) => { + const value = properties[key]; + const valueStr = typeof value === 'string' ? value : JSON.stringify(value); + return `${key}=${valueStr}`; + }) + .join(' '); + return ` {${formatted}}`; +} + +/** + * Formats the category prefix for log messages. + * Skips the root category (first element) and wraps remaining categories in brackets. + * @param category - The category array from the log record + * @returns The formatted category prefix string + */ +function formatCategoryPrefix(category: readonly string[]): string { + if (category.length <= 1) { + return ''; + } + return `[${category.slice(1).join('/')}]`; +} + +/** + * Creates a LogTape sink that writes to a VS Code LogOutputChannel. + * The sink supports all log levels including fatal (mapped to error). + * + * @param channelName - The name for the VS Code output channel + * @returns A VSCodeSink that can be used with LogTape's configure() + * + * @example + * ```typescript + * import { configure } from '@logtape/logtape'; + * import { createVSCodeSink } from './logtape-vscode-sink'; + * + * const vscodeChannel = createVSCodeSink('Verilog'); + * await configure({ + * sinks: { vscode: vscodeChannel }, + * loggers: [{ category: ['Verilog'], lowestLevel: 'debug', sinks: ['vscode'] }], + * }); + * ``` + */ +export function createVSCodeSink(channelName: string): VSCodeSink { + const channel = vscode.window.createOutputChannel(channelName, { log: true }); + + const sink = ((record: LogRecord) => { + const categoryPrefix = formatCategoryPrefix(record.category); + const message = formatMessage(record); + const properties = formatProperties(record.properties); + const formattedMessage = categoryPrefix + ? `${categoryPrefix} ${message}${properties}` + : `${message}${properties}`; + + switch (record.level) { + case 'debug': + channel.debug(formattedMessage); + break; + case 'info': + channel.info(formattedMessage); + break; + case 'warning': + channel.warn(formattedMessage); + break; + case 'error': + channel.error(formattedMessage); + break; + case 'fatal': + // VS Code LogOutputChannel doesn't have a fatal level, use error + channel.error(`[FATAL] ${formattedMessage}`); + break; + } + }) as VSCodeSink; + + sink.show = () => channel.show(); + sink.dispose = () => channel.dispose(); + + return sink; +} diff --git a/src/providers/CompletionItemProvider.ts b/src/providers/CompletionItemProvider.ts index efb8f969..587557f4 100644 --- a/src/providers/CompletionItemProvider.ts +++ b/src/providers/CompletionItemProvider.ts @@ -1,15 +1,13 @@ // SPDX-License-Identifier: MIT import * as vscode from 'vscode'; import { CtagsManager, Symbol } from '../ctags'; -import { Logger } from '../logger'; +import { getExtensionLogger } from '../logging'; import { END_OF_LINE } from '../constants'; export class VerilogCompletionItemProvider implements vscode.CompletionItemProvider { - private logger: Logger; + private readonly logger = getExtensionLogger('Provider', 'CompletionItem'); private ctagsManager: CtagsManager; - constructor(logger: Logger, - ctagsManager: CtagsManager){ - this.logger = logger; + constructor(ctagsManager: CtagsManager) { this.ctagsManager = ctagsManager; } @@ -20,7 +18,7 @@ export class VerilogCompletionItemProvider implements vscode.CompletionItemProvi _token: vscode.CancellationToken, _context: vscode.CompletionContext ): Promise { - this.logger.info('Completion items requested'); + this.logger.info("Completion items requested", { uri: document.uri.toString() }); const items: vscode.CompletionItem[] = []; const symbols: Symbol[] = await this.ctagsManager.getSymbols(document); @@ -42,7 +40,7 @@ export class VerilogCompletionItemProvider implements vscode.CompletionItemProvi newItem.documentation = new vscode.MarkdownString(doc); items.push(newItem); }); - this.logger.info(`${items.length } items requested`); + this.logger.info("Completion items returned", { count: items.length }); return items; } diff --git a/src/providers/DefinitionProvider.ts b/src/providers/DefinitionProvider.ts index 081e8062..5a1c0689 100644 --- a/src/providers/DefinitionProvider.ts +++ b/src/providers/DefinitionProvider.ts @@ -1,16 +1,14 @@ // SPDX-License-Identifier: MIT import * as vscode from 'vscode'; import { CtagsManager } from '../ctags'; -import { Logger } from '../logger'; +import { getExtensionLogger } from '../logging'; export class VerilogDefinitionProvider implements vscode.DefinitionProvider { - private logger: Logger; + private readonly logger = getExtensionLogger('Provider', 'Definition'); private ctagsManager: CtagsManager; - constructor(logger: Logger, - ctagsManager: CtagsManager){ - this.logger = logger; + constructor(ctagsManager: CtagsManager) { this.ctagsManager = ctagsManager; } @@ -19,10 +17,10 @@ export class VerilogDefinitionProvider implements vscode.DefinitionProvider { position: vscode.Position, _token: vscode.CancellationToken ): Promise { - this.logger.info(`Definitions Requested: ${ document.uri}`); + this.logger.info("Definitions requested", { uri: document.uri.toString() }); // find all matching symbols const definitions: vscode.DefinitionLink[] = await this.ctagsManager.findSymbol(document, position); - this.logger.info(`${definitions.length } definitions returned`); + this.logger.info("Definitions returned", { count: definitions.length }); return definitions; } } diff --git a/src/providers/DocumentSymbolProvider.ts b/src/providers/DocumentSymbolProvider.ts index df719ac4..2d97b8d2 100644 --- a/src/providers/DocumentSymbolProvider.ts +++ b/src/providers/DocumentSymbolProvider.ts @@ -1,16 +1,14 @@ // SPDX-License-Identifier: MIT import * as vscode from 'vscode'; import { CtagsManager, Symbol } from '../ctags'; -import { Logger } from '../logger'; +import { getExtensionLogger } from '../logging'; export class VerilogDocumentSymbolProvider implements vscode.DocumentSymbolProvider { public docSymbols: vscode.DocumentSymbol[] = []; - private logger: Logger; + private readonly logger = getExtensionLogger('Provider', 'DocumentSymbol'); private ctagsManager: CtagsManager; - constructor(logger: Logger, - ctagsManager: CtagsManager){ - this.logger = logger; + constructor(ctagsManager: CtagsManager) { this.ctagsManager = ctagsManager; } @@ -18,10 +16,10 @@ export class VerilogDocumentSymbolProvider implements vscode.DocumentSymbolProvi document: vscode.TextDocument, _token: vscode.CancellationToken ): Promise { - this.logger.info(`[VerilogSymbol] Symbols Requested: ${ document.uri}`); + this.logger.info("Symbols requested", { uri: document.uri.toString() }); const symbols: Symbol[] = await this.ctagsManager.getSymbols(document); this.docSymbols = this.buildDocumentSymbolList(symbols); - this.logger.info(`${this.docSymbols.length } top-level symbols returned`); + this.logger.info("Symbols returned", { count: this.docSymbols.length }); return this.docSymbols; } diff --git a/src/providers/FormatProvider.ts b/src/providers/FormatProvider.ts index 2762a0fb..1e3bb456 100644 --- a/src/providers/FormatProvider.ts +++ b/src/providers/FormatProvider.ts @@ -5,7 +5,8 @@ import * as fs from 'fs'; import * as os from 'os'; import * as crypto from 'crypto'; import * as path from 'path'; -import { Logger } from '../logger'; +import { type Logger } from '@logtape/logtape'; +import { getExtensionLogger } from '../logging'; // handle temporary file class TemporaryFile { @@ -60,13 +61,13 @@ abstract class FileBasedFormattingEditProvider implements vscode.DocumentFormatt // create temporary file and copy document to it const tempFile: TemporaryFile = new TemporaryFile(this.namespace, this.tmpFileExt); tempFile.writeFileSync(document.getText(), { flag: 'w' }); - this.logger.info(`Temp file created at:${ tempFile.path}`); + this.logger.info("Temp file created", { path: tempFile.path }); const args: string[] = this.prepareArgument(tempFile.path); // execute command const binPath: string = this.config.get('path', ''); - this.logger.info(`Executing command: ${ binPath } ${ args.join(' ')}`); + this.logger.info("Executing formatter", { binPath, args }); try { const cwd = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; execFileSync(binPath, args, cwd ? { cwd } : undefined); @@ -78,9 +79,9 @@ abstract class FileBasedFormattingEditProvider implements vscode.DocumentFormatt tempFile.dispose(); return [vscode.TextEdit.replace(wholeFileRange, formattedText)]; } catch (err) { - this.logger.error(String(err)); + this.logger.error("Formatter execution failed", { error: String(err) }); if (err instanceof Error && err.stack) { - this.logger.error(err.stack); + this.logger.error("Stack trace", { stack: err.stack }); } } @@ -141,11 +142,7 @@ class VeribleVerilogFormatEditProvider extends FileBasedFormattingEditProvider { } export class VerilogFormatProvider implements vscode.DocumentFormattingEditProvider { - private logger: Logger; - - constructor(logger: Logger) { - this.logger = logger; - } + private readonly logger = getExtensionLogger('Provider', 'Format', 'Verilog'); provideDocumentFormattingEdits( document: vscode.TextDocument, @@ -181,11 +178,7 @@ export class VerilogFormatProvider implements vscode.DocumentFormattingEditProvi } export class SystemVerilogFormatProvider implements vscode.DocumentFormattingEditProvider { - private logger: Logger; - - constructor(logger: Logger) { - this.logger = logger; - } + private readonly logger = getExtensionLogger('Provider', 'Format', 'SystemVerilog'); provideDocumentFormattingEdits( document: vscode.TextDocument, diff --git a/src/providers/HoverProvider.ts b/src/providers/HoverProvider.ts index f49d826a..3b9ca3b5 100644 --- a/src/providers/HoverProvider.ts +++ b/src/providers/HoverProvider.ts @@ -1,15 +1,13 @@ // SPDX-License-Identifier: MIT import * as vscode from 'vscode'; import { CtagsManager } from '../ctags'; -import { Logger } from '../logger'; +import { getExtensionLogger } from '../logging'; export class VerilogHoverProvider implements vscode.HoverProvider { // lang: verilog / systemverilog - private logger: Logger; + private readonly logger = getExtensionLogger('Provider', 'Hover'); private ctagsManager: CtagsManager; - constructor(logger: Logger, - ctagsManager: CtagsManager){ - this.logger = logger; + constructor(ctagsManager: CtagsManager) { this.ctagsManager = ctagsManager; } @@ -18,7 +16,7 @@ export class VerilogHoverProvider implements vscode.HoverProvider { position: vscode.Position, _token: vscode.CancellationToken ): Promise { - this.logger.info('Hover requested'); + this.logger.info("Hover requested", { uri: document.uri.toString() }); const matches: vscode.DefinitionLink[] = await this.ctagsManager.findSymbol(document, position); // find symbol for (const i of matches) { @@ -32,10 +30,10 @@ export class VerilogHoverProvider implements vscode.HoverProvider { const code = doc.getText(i.targetRange).trim(); const hoverText: vscode.MarkdownString = new vscode.MarkdownString(); hoverText.appendCodeblock(code, document.languageId); - this.logger.info('Hover object returned'); + this.logger.info("Hover returned"); return new vscode.Hover(hoverText); } - this.logger.warn('Hover object not found'); + this.logger.warn("Hover not found"); return undefined; } } diff --git a/src/test/ctags.test.ts b/src/test/ctags.test.ts index 72c9e571..84af33b1 100644 --- a/src/test/ctags.test.ts +++ b/src/test/ctags.test.ts @@ -3,7 +3,6 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import { VerilogCompletionItemProvider } from '../providers/CompletionItemProvider'; import { CtagsManager, Symbol } from '../ctags'; -import { createLogger } from '../logger'; suite('Ctags Completion', () => { test('ctags completion items include symbols and docs', async () => { @@ -21,8 +20,7 @@ suite('Ctags Completion', () => { getSymbols: async () => symbols, } as unknown as CtagsManager; - const logger = createLogger('CompletionTest'); - const provider = new VerilogCompletionItemProvider(logger, ctagsManager); + const provider = new VerilogCompletionItemProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const items = await provider.provideCompletionItems( document, @@ -62,8 +60,7 @@ suite('Ctags Completion', () => { getSymbols: async () => symbols, } as unknown as CtagsManager; - const logger = createLogger('CompletionTestKinds'); - const provider = new VerilogCompletionItemProvider(logger, ctagsManager); + const provider = new VerilogCompletionItemProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const items = await provider.provideCompletionItems( document, @@ -100,8 +97,7 @@ suite('Ctags Completion', () => { getSymbols: async () => symbols, } as unknown as CtagsManager; - const logger = createLogger('CompletionTestParamsPorts'); - const provider = new VerilogCompletionItemProvider(logger, ctagsManager); + const provider = new VerilogCompletionItemProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const items = await provider.provideCompletionItems( document, @@ -139,8 +135,7 @@ suite('Ctags Completion', () => { getSymbols: async () => symbols, } as unknown as CtagsManager; - const logger = createLogger('CompletionTestKinds2'); - const provider = new VerilogCompletionItemProvider(logger, ctagsManager); + const provider = new VerilogCompletionItemProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const items = await provider.provideCompletionItems( document, @@ -173,8 +168,7 @@ suite('Ctags Completion', () => { getSymbols: async () => symbols, } as unknown as CtagsManager; - const logger = createLogger('CompletionTestScopesOrder'); - const provider = new VerilogCompletionItemProvider(logger, ctagsManager); + const provider = new VerilogCompletionItemProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const items = await provider.provideCompletionItems( document, @@ -213,8 +207,7 @@ suite('Ctags Completion', () => { getSymbols: async () => symbols, } as unknown as CtagsManager; - const logger = createLogger('CompletionTestTypes'); - const provider = new VerilogCompletionItemProvider(logger, ctagsManager); + const provider = new VerilogCompletionItemProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const items = await provider.provideCompletionItems( document, diff --git a/src/test/definitionProvider.test.ts b/src/test/definitionProvider.test.ts index 0e110612..03185584 100644 --- a/src/test/definitionProvider.test.ts +++ b/src/test/definitionProvider.test.ts @@ -3,7 +3,6 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import { VerilogDefinitionProvider } from '../providers/DefinitionProvider'; import { CtagsManager } from '../ctags'; -import { createLogger } from '../logger'; import { END_OF_LINE } from '../constants'; suite('DefinitionProvider', () => { @@ -29,8 +28,7 @@ suite('DefinitionProvider', () => { findSymbol: async () => definitionLinks, } as unknown as CtagsManager; - const logger = createLogger('DefinitionTest'); - const provider = new VerilogDefinitionProvider(logger, ctagsManager); + const provider = new VerilogDefinitionProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); // Position on 'sig' in the assign statement @@ -55,8 +53,7 @@ suite('DefinitionProvider', () => { findSymbol: async () => [], } as unknown as CtagsManager; - const logger = createLogger('DefinitionEmptyTest'); - const provider = new VerilogDefinitionProvider(logger, ctagsManager); + const provider = new VerilogDefinitionProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const result = await provider.provideDefinition( @@ -95,8 +92,7 @@ suite('DefinitionProvider', () => { findSymbol: async () => definitionLinks, } as unknown as CtagsManager; - const logger = createLogger('DefinitionMultiTest'); - const provider = new VerilogDefinitionProvider(logger, ctagsManager); + const provider = new VerilogDefinitionProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const result = await provider.provideDefinition( diff --git a/src/test/documentSymbolProvider.test.ts b/src/test/documentSymbolProvider.test.ts index 05eef144..cee82288 100644 --- a/src/test/documentSymbolProvider.test.ts +++ b/src/test/documentSymbolProvider.test.ts @@ -3,7 +3,6 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import { VerilogDocumentSymbolProvider } from '../providers/DocumentSymbolProvider'; import { CtagsManager, Symbol } from '../ctags'; -import { createLogger } from '../logger'; suite('DocumentSymbolProvider', () => { test('provides document symbols for module', async () => { @@ -21,8 +20,7 @@ suite('DocumentSymbolProvider', () => { getSymbols: async () => symbols, } as unknown as CtagsManager; - const logger = createLogger('DocumentSymbolTest'); - const provider = new VerilogDocumentSymbolProvider(logger, ctagsManager); + const provider = new VerilogDocumentSymbolProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const result = await provider.provideDocumentSymbols(document, tokenSource.token); @@ -54,8 +52,7 @@ suite('DocumentSymbolProvider', () => { getSymbols: async () => symbols, } as unknown as CtagsManager; - const logger = createLogger('DocumentSymbolHierarchyTest'); - const provider = new VerilogDocumentSymbolProvider(logger, ctagsManager); + const provider = new VerilogDocumentSymbolProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const result = await provider.provideDocumentSymbols(document, tokenSource.token); @@ -77,8 +74,7 @@ suite('DocumentSymbolProvider', () => { getSymbols: async () => [], } as unknown as CtagsManager; - const logger = createLogger('DocumentSymbolEmptyTest'); - const provider = new VerilogDocumentSymbolProvider(logger, ctagsManager); + const provider = new VerilogDocumentSymbolProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const result = await provider.provideDocumentSymbols(document, tokenSource.token); @@ -87,8 +83,7 @@ suite('DocumentSymbolProvider', () => { test('isContainer returns correct values for symbol kinds', () => { const ctagsManager = {} as unknown as CtagsManager; - const logger = createLogger('IsContainerTest'); - const provider = new VerilogDocumentSymbolProvider(logger, ctagsManager); + const provider = new VerilogDocumentSymbolProvider(ctagsManager); // Container types should return true assert.strictEqual(provider.isContainer(vscode.SymbolKind.Module), true); @@ -119,8 +114,7 @@ suite('DocumentSymbolProvider', () => { getSymbols: async () => symbols, } as unknown as CtagsManager; - const logger = createLogger('DocumentSymbolMultiModuleTest'); - const provider = new VerilogDocumentSymbolProvider(logger, ctagsManager); + const provider = new VerilogDocumentSymbolProvider(ctagsManager); const tokenSource = new vscode.CancellationTokenSource(); const result = await provider.provideDocumentSymbols(document, tokenSource.token); diff --git a/src/test/format.test.ts b/src/test/format.test.ts index d0ba6a94..2aa97e93 100644 --- a/src/test/format.test.ts +++ b/src/test/format.test.ts @@ -7,7 +7,6 @@ import * as path from 'path'; import * as vscode from 'vscode'; import which from 'which'; import { SystemVerilogFormatProvider } from '../providers/FormatProvider'; -import { createLogger } from '../logger'; suite('Formatting', () => { test('verible-verilog-format formats via configured binary', async function () { @@ -46,7 +45,7 @@ suite('Formatting', () => { content: input, }); - const provider = new SystemVerilogFormatProvider(createLogger('FormatTest')); + const provider = new SystemVerilogFormatProvider(); const tokenSource = new vscode.CancellationTokenSource(); const edits = await provider.provideDocumentFormattingEdits( document, diff --git a/src/test/hover.test.ts b/src/test/hover.test.ts index 349f89ff..9e960cdc 100644 --- a/src/test/hover.test.ts +++ b/src/test/hover.test.ts @@ -3,7 +3,6 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import { VerilogHoverProvider } from '../providers/HoverProvider'; import { CtagsManager } from '../ctags'; -import { createLogger } from '../logger'; import { END_OF_LINE } from '../constants'; suite('Hover Provider', () => { @@ -27,7 +26,7 @@ suite('Hover Provider', () => { ], } as unknown as CtagsManager; - const provider = new VerilogHoverProvider(createLogger('HoverTest'), ctagsManager); + const provider = new VerilogHoverProvider(ctagsManager); const hover = await provider.provideHover( document, new vscode.Position(0, 8), diff --git a/src/test/iverilog.test.ts b/src/test/iverilog.test.ts index 59795585..29749eb5 100644 --- a/src/test/iverilog.test.ts +++ b/src/test/iverilog.test.ts @@ -6,7 +6,6 @@ import * as path from 'path'; import * as vscode from 'vscode'; import which from 'which'; import IcarusLinter from '../linter/IcarusLinter'; -import { createLogger } from '../logger'; async function waitForDiagnostics( collection: vscode.DiagnosticCollection, @@ -33,7 +32,7 @@ suite('Icarus Linter', () => { } const diagnostics = vscode.languages.createDiagnosticCollection('iverilog-severity-test'); - const linter = new TestLinter(diagnostics, createLogger('IcarusSeverityTest')); + const linter = new TestLinter(diagnostics); assert.strictEqual(linter.mapSeverity('error'), vscode.DiagnosticSeverity.Error); assert.strictEqual(linter.mapSeverity('warning'), vscode.DiagnosticSeverity.Warning); @@ -66,7 +65,7 @@ suite('Icarus Linter', () => { await iverilogConfig.update('runAtFileLocation', true, vscode.ConfigurationTarget.Global); const diagnostics = vscode.languages.createDiagnosticCollection('iverilog-test'); - const linter = new IcarusLinter(diagnostics, createLogger('IcarusLinterTest')); + const linter = new IcarusLinter(diagnostics); const document = await vscode.workspace.openTextDocument(tempFilePath); linter.startLint(document); diff --git a/src/test/languageServer.test.ts b/src/test/languageServer.test.ts index bb9422ef..9971a5a7 100644 --- a/src/test/languageServer.test.ts +++ b/src/test/languageServer.test.ts @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT import * as assert from 'assert'; -import { createLogger } from '../logger'; import { createLanguageServerDefinitions } from '../languageServer/definitions'; import { initAllLanguageClients, stopAllLanguageClients } from '../languageServer'; @@ -32,8 +31,7 @@ suite('Language Server smoke', () => { }); test('initializes and stops without enabled servers', async () => { - const logger = createLogger('Verilog-LS-Test'); - initAllLanguageClients(logger); + initAllLanguageClients(); await stopAllLanguageClients(); }); }); diff --git a/src/test/logTestUtils.ts b/src/test/logTestUtils.ts new file mode 100644 index 00000000..28026034 --- /dev/null +++ b/src/test/logTestUtils.ts @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: MIT +import { type LogRecord, configure, reset } from '@logtape/logtape'; +import { ROOT_LOGGER_CATEGORY } from '../logging'; + +/** + * Test utility for capturing and inspecting log output during tests. + * Provides a buffer sink for LogTape and helper methods for assertions. + * + * @example + * ```typescript + * import { LogCapture } from './logTestUtils'; + * + * suite('My Test Suite', () => { + * const logs = new LogCapture(); + * + * suiteSetup(async () => await logs.setup()); + * setup(() => logs.clear()); + * suiteTeardown(async () => await logs.teardown()); + * + * test('logs error on failure', async () => { + * // ... test code that triggers logging + * assert.ok(logs.hasError('expected error message')); + * }); + * }); + * ``` + */ +export class LogCapture { + /** All captured log records */ + public records: LogRecord[] = []; + + /** + * Sets up LogTape with a buffer sink for testing. + * Call this in suiteSetup(). + */ + async setup(): Promise { + await configure({ + sinks: { test: this.records.push.bind(this.records) }, + filters: {}, + loggers: [{ category: [ROOT_LOGGER_CATEGORY], sinks: ['test'], level: 'debug' }], + }); + } + + /** + * Resets LogTape configuration. + * Call this in suiteTeardown(). + */ + async teardown(): Promise { + await reset(); + } + + /** + * Clears all captured log records. + * Call this in setup() before each test. + */ + clear(): void { + this.records.length = 0; + } + + /** + * Formats a log record's message into a single string. + * @param record - The log record to format + * @returns The formatted message string + */ + private formatMessage(record: LogRecord): string { + return record.message + .map((part) => (typeof part === 'string' ? part : JSON.stringify(part))) + .join(''); + } + + /** + * Gets all log messages as an array of strings. + * @returns Array of formatted log messages + */ + getMessages(): string[] { + return this.records.map((r) => this.formatMessage(r)); + } + + /** + * Gets log records filtered by level. + * @param level - The log level to filter by + * @returns Array of matching log records + */ + getByLevel(level: 'trace' | 'debug' | 'info' | 'warning' | 'error' | 'fatal'): LogRecord[] { + return this.records.filter((r) => r.level === level); + } + + /** + * Gets log records filtered by category prefix. + * @param category - The category array prefix to match + * @returns Array of matching log records + */ + getByCategory(category: string[]): LogRecord[] { + return this.records.filter( + (r) => + r.category.length >= category.length && + category.every((c, i) => r.category[i] === c) + ); + } + + /** + * Checks if any log message contains a substring. + * @param substring - The substring to search for (optional) + * @returns True if any message contains the substring, or if any messages exist when no substring provided + */ + hasMessage(substring?: string): boolean { + if (substring === undefined) { + return this.records.length > 0; + } + return this.getMessages().some((m) => m.includes(substring)); + } + + /** + * Alias for hasMessage - checks if any log message contains a substring. + * @param substring - The substring to search for + * @returns True if any message contains the substring + */ + hasMessageContaining(substring: string): boolean { + return this.hasMessage(substring); + } + + /** + * Checks if any log record exists at the specified level. + * @param level - The log level to check + * @returns True if any log record exists at that level + */ + hasLevel(level: 'trace' | 'debug' | 'info' | 'warning' | 'error' | 'fatal'): boolean { + return this.getByLevel(level).length > 0; + } + + /** + * Checks if any error-level log message contains a substring. + * @param substring - The substring to search for (optional) + * @returns True if any error message contains the substring, or if any errors exist when no substring provided + */ + hasError(substring?: string): boolean { + const errors = this.getByLevel('error'); + if (substring === undefined) { + return errors.length > 0; + } + return errors.some((r) => this.formatMessage(r).includes(substring)); + } + + /** + * Checks if any warning-level log message contains a substring. + * @param substring - The substring to search for (optional) + * @returns True if any warning message contains the substring, or if any warnings exist when no substring provided + */ + hasWarning(substring?: string): boolean { + const warnings = this.getByLevel('warning'); + if (substring === undefined) { + return warnings.length > 0; + } + return warnings.some((r) => this.formatMessage(r).includes(substring)); + } + + /** + * Checks if any info-level log message contains a substring. + * @param substring - The substring to search for (optional) + * @returns True if any info message contains the substring, or if any infos exist when no substring provided + */ + hasInfo(substring?: string): boolean { + const infos = this.getByLevel('info'); + if (substring === undefined) { + return infos.length > 0; + } + return infos.some((r) => this.formatMessage(r).includes(substring)); + } + + /** + * Checks if any fatal-level log message contains a substring. + * @param substring - The substring to search for (optional) + * @returns True if any fatal message contains the substring, or if any fatals exist when no substring provided + */ + hasFatal(substring?: string): boolean { + const fatals = this.getByLevel('fatal'); + if (substring === undefined) { + return fatals.length > 0; + } + return fatals.some((r) => this.formatMessage(r).includes(substring)); + } +} diff --git a/src/test/logger.test.ts b/src/test/logger.test.ts deleted file mode 100644 index e48aa351..00000000 --- a/src/test/logger.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: MIT -import * as assert from 'assert'; -import { Logger } from '../logger'; - -// Mock LogOutputChannel for testing -class MockOutputChannel { - public messages: { level: string; message: string }[] = []; - - trace(message: string): void { - this.messages.push({ level: 'trace', message }); - } - info(message: string): void { - this.messages.push({ level: 'info', message }); - } - debug(message: string): void { - this.messages.push({ level: 'debug', message }); - } - warn(message: string): void { - this.messages.push({ level: 'warn', message }); - } - error(message: string): void { - this.messages.push({ level: 'error', message }); - } - show(): void { - // no-op for testing - } -} - -suite('Logger', () => { - test('logs messages at different levels', () => { - const mockChannel = new MockOutputChannel(); - const logger = new Logger('Test', mockChannel as never); - - logger.trace('trace message'); - logger.debug('debug message'); - logger.info('info message'); - logger.warn('warn message'); - logger.error('error message'); - - assert.strictEqual(mockChannel.messages.length, 5); - assert.strictEqual(mockChannel.messages[0].level, 'trace'); - assert.strictEqual(mockChannel.messages[0].message, 'trace message'); - assert.strictEqual(mockChannel.messages[1].level, 'debug'); - assert.strictEqual(mockChannel.messages[2].level, 'info'); - assert.strictEqual(mockChannel.messages[3].level, 'warn'); - assert.strictEqual(mockChannel.messages[4].level, 'error'); - }); - - test('child logger adds prefix to messages', () => { - const mockChannel = new MockOutputChannel(); - const parentLogger = new Logger('Parent', mockChannel as never); - const childLogger = parentLogger.getChild('Child'); - - childLogger.info('child message'); - - assert.strictEqual(mockChannel.messages.length, 1); - assert.ok( - mockChannel.messages[0].message.includes('[Child]'), - 'Child message should include child name prefix' - ); - assert.ok( - mockChannel.messages[0].message.includes('child message'), - 'Child message should include the message content' - ); - }); - - test('nested child loggers add multiple prefixes', () => { - const mockChannel = new MockOutputChannel(); - const parentLogger = new Logger('Parent', mockChannel as never); - const childLogger = parentLogger.getChild('Child'); - const grandchildLogger = childLogger.getChild('Grandchild'); - - grandchildLogger.info('nested message'); - - assert.strictEqual(mockChannel.messages.length, 1); - const message = mockChannel.messages[0].message; - assert.ok(message.includes('[Grandchild]'), 'Should include grandchild prefix'); - assert.ok(message.includes('[Child]'), 'Should include child prefix'); - }); - - test('logs data as JSON when provided', () => { - const mockChannel = new MockOutputChannel(); - const logger = new Logger('Test', mockChannel as never); - - const testData = { key: 'value', count: 42 }; - logger.info('message with data', testData); - - assert.strictEqual(mockChannel.messages.length, 1); - const message = mockChannel.messages[0].message; - assert.ok(message.includes('message with data'), 'Should include message'); - assert.ok(message.includes('"key":"value"'), 'Should include JSON data'); - assert.ok(message.includes('"count":42'), 'Should include JSON data'); - }); - - test('show delegates to parent channel', () => { - let showCalled = false; - const mockChannel = new MockOutputChannel(); - mockChannel.show = () => { - showCalled = true; - }; - - const logger = new Logger('Test', mockChannel as never); - logger.show(); - - assert.ok(showCalled, 'show() should be called on parent channel'); - }); - - test('child logger show delegates to parent', () => { - let showCalled = false; - const mockChannel = new MockOutputChannel(); - mockChannel.show = () => { - showCalled = true; - }; - - const parentLogger = new Logger('Parent', mockChannel as never); - const childLogger = parentLogger.getChild('Child'); - childLogger.show(); - - assert.ok(showCalled, 'show() should propagate through parent chain'); - }); -}); diff --git a/src/test/logtape.test.ts b/src/test/logtape.test.ts new file mode 100644 index 00000000..ce2b9f5b --- /dev/null +++ b/src/test/logtape.test.ts @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +import * as assert from 'assert'; +import { getLogger } from '@logtape/logtape'; +import { ROOT_LOGGER_CATEGORY } from '../logging'; +import { LogCapture } from './logTestUtils'; + +suite('LogTape Integration', () => { + const logCapture = new LogCapture(); + + suiteSetup(async () => { + await logCapture.setup(); + }); + + suiteTeardown(async () => { + await logCapture.teardown(); + }); + + setup(() => { + logCapture.clear(); + }); + + test('captures log messages at different levels', () => { + const logger = getLogger([ROOT_LOGGER_CATEGORY, 'Test', 'Levels']); + + logger.debug`debug message`; + logger.info`info message`; + logger.warn`warn message`; + logger.error`error message`; + logger.fatal`fatal message`; + + const messages = logCapture.getMessages(); + assert.strictEqual(messages.length, 5); + assert.ok(logCapture.hasLevel('debug')); + assert.ok(logCapture.hasInfo()); + assert.ok(logCapture.hasWarning()); + assert.ok(logCapture.hasError()); + assert.ok(logCapture.hasFatal()); + }); + + test('captures messages with interpolated values', () => { + const logger = getLogger([ROOT_LOGGER_CATEGORY, 'Test', 'Interpolation']); + const value = 42; + const name = 'test'; + + logger.info`Processing ${name} with value ${value}`; + + const messages = logCapture.getMessages(); + assert.strictEqual(messages.length, 1); + assert.ok(messages[0].includes('Processing')); + assert.ok(messages[0].includes('test')); + assert.ok(messages[0].includes('42')); + }); + + test('clear() removes all captured messages', () => { + const logger = getLogger([ROOT_LOGGER_CATEGORY, 'Test', 'Clear']); + + logger.info`first message`; + logger.info`second message`; + assert.strictEqual(logCapture.getMessages().length, 2); + + logCapture.clear(); + assert.strictEqual(logCapture.getMessages().length, 0); + }); + + test('hierarchical category filtering', () => { + const parentLogger = getLogger([ROOT_LOGGER_CATEGORY, 'Parent']); + const childLogger = getLogger([ROOT_LOGGER_CATEGORY, 'Parent', 'Child']); + const siblingLogger = getLogger([ROOT_LOGGER_CATEGORY, 'Other']); + + parentLogger.info`parent message`; + childLogger.info`child message`; + siblingLogger.info`sibling message`; + + const messages = logCapture.getMessages(); + assert.strictEqual(messages.length, 3); + }); + + test('hasMessageContaining finds matching messages', () => { + const logger = getLogger([ROOT_LOGGER_CATEGORY, 'Test', 'Contains']); + + logger.info`The quick brown fox`; + logger.error`Something went wrong`; + + assert.ok(logCapture.hasMessageContaining('brown fox')); + assert.ok(logCapture.hasMessageContaining('went wrong')); + assert.ok(!logCapture.hasMessageContaining('nonexistent')); + }); +}); diff --git a/src/test/moduleInstantiation.test.ts b/src/test/moduleInstantiation.test.ts index c50cb17f..6c02b3d3 100644 --- a/src/test/moduleInstantiation.test.ts +++ b/src/test/moduleInstantiation.test.ts @@ -6,8 +6,6 @@ import * as os from 'os'; import * as path from 'path'; import * as vscode from 'vscode'; import which from 'which'; -import * as extension from '../extension'; -import { createLogger } from '../logger'; import { instantiateModule } from '../commands/ModuleInstantiation'; suite('Module Instantiation', () => { @@ -50,8 +48,6 @@ suite('Module Instantiation', () => { return; } - (extension as { logger: unknown }).logger = createLogger('ModuleInstantiationTest'); - const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ctags-inst-')); const tempFilePath = path.join(tempRoot, 'mod.sv'); const source = [ diff --git a/src/test/verible-verilog-lint.test.ts b/src/test/verible-verilog-lint.test.ts index d2acfe40..e577e0bb 100644 --- a/src/test/verible-verilog-lint.test.ts +++ b/src/test/verible-verilog-lint.test.ts @@ -6,7 +6,6 @@ import * as path from 'path'; import * as vscode from 'vscode'; import which from 'which'; import VeribleVerilogLintLinter from '../linter/VeribleVerilogLintLinter'; -import { createLogger } from '../logger'; async function waitForDiagnostics( collection: vscode.DiagnosticCollection, @@ -50,8 +49,7 @@ suite('Verible Verilog Lint', () => { const diagnostics = vscode.languages.createDiagnosticCollection('verible-verilog-lint-test'); const linter = new VeribleVerilogLintLinter( - diagnostics, - createLogger('VeribleVerilogLintTest') + diagnostics ); const document = await vscode.workspace.openTextDocument(tempFilePath); diff --git a/src/test/verilator.test.ts b/src/test/verilator.test.ts index 75555518..9b25408c 100644 --- a/src/test/verilator.test.ts +++ b/src/test/verilator.test.ts @@ -6,7 +6,6 @@ import * as path from 'path'; import * as vscode from 'vscode'; import which from 'which'; import VerilatorLinter from '../linter/VerilatorLinter'; -import { createLogger } from '../logger'; async function waitForDiagnostics( collection: vscode.DiagnosticCollection, @@ -119,7 +118,7 @@ suite('Verilator Linter', () => { try { await withVerilatorConfig(verilatorPath, {}, async () => { const diagnostics = vscode.languages.createDiagnosticCollection('verilator-test'); - const linter = new VerilatorLinter(diagnostics, createLogger('VerilatorLinterTest')); + const linter = new VerilatorLinter(diagnostics); const document = await vscode.workspace.openTextDocument(tempFilePath); linter.startLint(document); @@ -150,7 +149,7 @@ suite('Verilator Linter', () => { try { await withVerilatorConfig(verilatorPath, { arguments: '-Wall' }, async () => { const diagnostics = vscode.languages.createDiagnosticCollection('verilator-warning-test'); - const linter = new VerilatorLinter(diagnostics, createLogger('VerilatorLinterWarningTest')); + const linter = new VerilatorLinter(diagnostics); const document = await vscode.workspace.openTextDocument(tempFilePath); linter.startLint(document); @@ -180,7 +179,7 @@ suite('Verilator Linter', () => { try { await withVerilatorConfig(verilatorPath, {}, async () => { const diagnostics = vscode.languages.createDiagnosticCollection('verilator-space-test'); - const linter = new VerilatorLinter(diagnostics, createLogger('VerilatorLinterSpaceTest')); + const linter = new VerilatorLinter(diagnostics); const document = await vscode.workspace.openTextDocument(tempFilePath); linter.startLint(document); @@ -211,7 +210,7 @@ suite('Verilator Linter', () => { try { await withVerilatorConfig(verilatorPath, { includePath: [tempRoot] }, async () => { const diagnostics = vscode.languages.createDiagnosticCollection('verilator-include-test'); - const linter = new VerilatorLinter(diagnostics, createLogger('VerilatorLinterIncludeTest')); + const linter = new VerilatorLinter(diagnostics); const document = await vscode.workspace.openTextDocument(tempFilePath); linter.startLint(document); @@ -251,10 +250,7 @@ suite('Verilator Linter', () => { const diagnostics = vscode.languages.createDiagnosticCollection( 'verilator-include-root-test' ); - const linter = new VerilatorLinter( - diagnostics, - createLogger('VerilatorLinterIncludeRootTest') - ); + const linter = new VerilatorLinter(diagnostics); const document = await vscode.workspace.openTextDocument(tempFilePath); linter.startLint(document); diff --git a/tsconfig.json b/tsconfig.json index 8417ba1e..45f655b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "module": "Node16", "target": "ES2022", "outDir": "out", - "lib": ["ES2020"], + "lib": ["ES2022"], "sourceMap": true, "rootDir": ".", "strict": true, /* enable all strict type-checking options */ From 8d40c01abb5a32f7da07d8a90ba220d3b103fcb2 Mon Sep 17 00:00:00 2001 From: Masahiro Hiramori Date: Fri, 16 Jan 2026 21:00:21 +0900 Subject: [PATCH 2/4] chore(deps): upgrade @logtape/logtape to v2.0.0 - Update @logtape/logtape from ^1.3.6 to ^2.0.0 - Replace deprecated 'level' property with 'lowestLevel' in logger config (breaking change in LogTape 1.0.0) --- package-lock.json | 151 +++++++++++++++++++++++++++++++++++---- package.json | 4 +- src/logging.ts | 2 +- src/test/logTestUtils.ts | 2 +- 4 files changed, 143 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 330e970f..641e21e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,7 @@ "name": "veriloghdl", "version": "1.22.2", "dependencies": { - "@logtape/logtape": "^1.3.6", - "@vscode/test-cli": "^0.0.12", + "@logtape/logtape": "^2.0.0", "js-yaml": "^4.1.1", "semver": "^7.7.3", "vscode-languageclient": "^9.0.1", @@ -22,6 +21,7 @@ "@types/which": "^3.0.4", "@typescript-eslint/eslint-plugin": "^8.39.1", "@typescript-eslint/parser": "^8.39.1", + "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.5.2", "esbuild": "^0.27.2", "eslint": "9.39.2", @@ -53,6 +53,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -736,6 +737,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -752,6 +754,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, @@ -762,12 +765,14 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -784,6 +789,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -798,6 +804,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -814,6 +821,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -837,6 +845,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -863,21 +872,23 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@logtape/logtape": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@logtape/logtape/-/logtape-1.3.6.tgz", - "integrity": "sha512-OaK8eal8zcjB0GZbllXKgUC2T9h/GyNLQyQXjJkf1yum7SZKTWs9gs/t8NMS0kVVaSnA7bhU0Sjws/Iy4e0/IQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@logtape/logtape/-/logtape-2.0.0.tgz", + "integrity": "sha512-z9Hp44mIRXAzgxSyQfFQiRuJ78EMnZa6g43UCxyGOO3RgHjn/7q+5OhdbhypkeHjiJRPxv6RmRsyF0S+OOYWnA==", "funding": [ "https://github.com/sponsors/dahlia" ], @@ -887,6 +898,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "optional": true, "engines": { "node": ">=14" @@ -930,6 +942,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, "license": "MIT" }, "node_modules/@types/json-schema": { @@ -949,6 +962,7 @@ "version": "10.0.10", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, "license": "MIT" }, "node_modules/@types/node": { @@ -1256,6 +1270,7 @@ "version": "0.0.12", "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.12.tgz", "integrity": "sha512-iYN0fDg29+a2Xelle/Y56Xvv7Nc8Thzq4VwpzAF/SIE6918rDicqfsQxV6w1ttr2+SOm+10laGuY9FG2ptEKsQ==", + "dev": true, "license": "MIT", "dependencies": { "@types/mocha": "^10.0.10", @@ -1279,6 +1294,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -1288,6 +1304,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -1308,6 +1325,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -1323,12 +1341,14 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, "license": "ISC" }, "node_modules/@vscode/test-cli/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -1344,6 +1364,7 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -1360,6 +1381,7 @@ "version": "10.2.2", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -1616,6 +1638,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -1624,6 +1647,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1636,6 +1660,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1806,6 +1831,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, "engines": { "node": ">=8" } @@ -1824,6 +1850,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1835,7 +1862,8 @@ "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true }, "node_modules/browserslist": { "version": "4.23.0", @@ -1880,6 +1908,7 @@ "version": "10.1.3", "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", + "dev": true, "license": "ISC", "dependencies": { "@bcoe/v8-coverage": "^1.0.1", @@ -1973,6 +2002,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, "engines": { "node": ">=10" }, @@ -2004,6 +2034,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -2020,6 +2051,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2035,6 +2067,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -2059,6 +2092,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -2108,6 +2142,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -2122,6 +2157,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2134,6 +2170,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/concat-map": { @@ -2146,6 +2183,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, "license": "MIT" }, "node_modules/core-util-is": { @@ -2159,6 +2197,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -2173,6 +2212,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -2241,6 +2281,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2258,6 +2299,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, "engines": { "node": ">=10" }, @@ -2309,6 +2351,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -2332,7 +2375,8 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/electron-to-chromium": { "version": "1.4.715", @@ -2343,12 +2387,14 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -2558,6 +2604,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, "engines": { "node": ">=6" } @@ -2566,6 +2613,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -3060,6 +3108,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -3072,6 +3121,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3087,6 +3137,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, "bin": { "flat": "cli.js" } @@ -3132,6 +3183,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -3148,6 +3200,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -3201,6 +3254,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -3393,7 +3447,8 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/has-bigints": { "version": "1.1.0", @@ -3412,6 +3467,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3490,6 +3546,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, "bin": { "he": "bin/he" } @@ -3498,6 +3555,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, "license": "MIT" }, "node_modules/http-proxy-agent": { @@ -3650,6 +3708,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -3742,6 +3801,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -3766,6 +3826,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -3793,6 +3854,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -3842,6 +3904,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -3868,6 +3931,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3877,6 +3941,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, "engines": { "node": ">=8" } @@ -3984,6 +4049,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, "engines": { "node": ">=10" }, @@ -4047,12 +4113,14 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=8" @@ -4062,6 +4130,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", @@ -4076,6 +4145,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", @@ -4255,6 +4325,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -4275,6 +4346,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -4290,6 +4362,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, "license": "MIT", "dependencies": { "semver": "^7.5.3" @@ -4399,6 +4472,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -4408,6 +4482,7 @@ "version": "11.7.5", "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "dev": true, "license": "MIT", "dependencies": { "browser-stdout": "^1.3.1", @@ -4444,6 +4519,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -4453,6 +4529,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -4468,6 +4545,7 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -4488,6 +4566,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -4503,12 +4582,14 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, "license": "ISC" }, "node_modules/mocha/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -4524,6 +4605,7 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -4540,6 +4622,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -4553,6 +4636,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -4568,6 +4652,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/natural-compare": { @@ -4592,6 +4677,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -4955,6 +5041,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -4969,6 +5056,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -4983,6 +5071,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/pako": { @@ -5009,6 +5098,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { "node": ">=8" } @@ -5017,6 +5107,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -5058,12 +5149,14 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -5123,6 +5216,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -5155,6 +5249,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -5210,6 +5305,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -5299,6 +5395,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, "funding": [ { "type": "github", @@ -5390,6 +5487,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" @@ -5454,6 +5552,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -5465,6 +5564,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -5562,6 +5662,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -5620,6 +5721,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5634,6 +5736,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5705,6 +5808,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5717,6 +5821,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5737,6 +5842,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { "node": ">=8" }, @@ -5748,6 +5854,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -5772,6 +5879,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -5838,6 +5946,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", @@ -5852,6 +5961,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -5861,6 +5971,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -5881,6 +5992,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -5896,12 +6008,14 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, "license": "ISC" }, "node_modules/test-exclude/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -5917,6 +6031,7 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -5982,6 +6097,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -6286,6 +6402,7 @@ "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, "license": "ISC", "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", @@ -6544,12 +6661,14 @@ "version": "9.3.4", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -6568,6 +6687,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -6584,6 +6704,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -6598,6 +6719,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -6613,6 +6735,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "engines": { "node": ">=10" } @@ -6621,6 +6744,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -6639,6 +6763,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -6648,6 +6773,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -6662,6 +6788,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index ff5c1c46..646957d5 100644 --- a/package.json +++ b/package.json @@ -682,20 +682,20 @@ "syntax": "js-yaml ./syntaxes/systemverilog.tmLanguage.yaml >./syntaxes/systemverilog.tmLanguage.json" }, "dependencies": { - "@logtape/logtape": "^1.3.6", + "@logtape/logtape": "^2.0.0", "js-yaml": "^4.1.1", "semver": "^7.7.3", "vscode-languageclient": "^9.0.1", "which": "^6.0.0" }, "devDependencies": { - "@vscode/test-cli": "^0.0.12", "@types/node": "~25.0.3", "@types/semver": "^7.7.1", "@types/vscode": "^1.107.0", "@types/which": "^3.0.4", "@typescript-eslint/eslint-plugin": "^8.39.1", "@typescript-eslint/parser": "^8.39.1", + "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.5.2", "esbuild": "^0.27.2", "eslint": "9.39.2", diff --git a/src/logging.ts b/src/logging.ts index b436a2b7..e073febc 100644 --- a/src/logging.ts +++ b/src/logging.ts @@ -23,7 +23,7 @@ export async function bootstrapLogging(): Promise { await configure({ sinks: { vscode: vscodeSink }, filters: {}, - loggers: [{ category: [ROOT_LOGGER_CATEGORY], level: 'debug', sinks: ['vscode'] }], + loggers: [{ category: [ROOT_LOGGER_CATEGORY], lowestLevel: 'debug', sinks: ['vscode'] }], }); configured = true; } diff --git a/src/test/logTestUtils.ts b/src/test/logTestUtils.ts index 28026034..cb919657 100644 --- a/src/test/logTestUtils.ts +++ b/src/test/logTestUtils.ts @@ -36,7 +36,7 @@ export class LogCapture { await configure({ sinks: { test: this.records.push.bind(this.records) }, filters: {}, - loggers: [{ category: [ROOT_LOGGER_CATEGORY], sinks: ['test'], level: 'debug' }], + loggers: [{ category: [ROOT_LOGGER_CATEGORY], sinks: ['test'], lowestLevel: 'debug' }], }); } From 3999f5df587b43de2d9a3a3f24faa10df9a983cf Mon Sep 17 00:00:00 2001 From: Masahiro Hiramori Date: Fri, 16 Jan 2026 21:07:04 +0900 Subject: [PATCH 3/4] Update src/linter/XvlogLinter.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/linter/XvlogLinter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linter/XvlogLinter.ts b/src/linter/XvlogLinter.ts index 819031e1..ae538482 100644 --- a/src/linter/XvlogLinter.ts +++ b/src/linter/XvlogLinter.ts @@ -66,7 +66,7 @@ export default class XvlogLinter extends BaseLinter { diagnostics.push(diagnostic); }); - this.logger.info`${diagnostics.length} errors/warnings returned`; + this.logger.info(`${diagnostics.length} errors/warnings returned`); this.diagnosticCollection.set(doc.uri, diagnostics); }); } From 2c24496d43bce7dccaa4d4c4444ced6864d454a5 Mon Sep 17 00:00:00 2001 From: Masahiro Hiramori Date: Fri, 16 Jan 2026 21:07:26 +0900 Subject: [PATCH 4/4] Update src/providers/HoverProvider.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/providers/HoverProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/HoverProvider.ts b/src/providers/HoverProvider.ts index 3b9ca3b5..4b7044cb 100644 --- a/src/providers/HoverProvider.ts +++ b/src/providers/HoverProvider.ts @@ -16,7 +16,7 @@ export class VerilogHoverProvider implements vscode.HoverProvider { position: vscode.Position, _token: vscode.CancellationToken ): Promise { - this.logger.info("Hover requested", { uri: document.uri.toString() }); + this.logger.info(`Hover requested for ${document.uri.toString()}`); const matches: vscode.DefinitionLink[] = await this.ctagsManager.findSymbol(document, position); // find symbol for (const i of matches) {