From b1b83f45d964667bdd7c5fd1d96259a682c6893b Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 25 Jan 2025 14:50:53 -0500
Subject: [PATCH 01/83] temporary data type fix for multi files
---
src/api/backend.ts | 24 +++---
src/commands/refactorSmell.ts | 141 ++++++++++++++++++++--------------
src/global.d.ts | 62 +++++++--------
src/types.ts | 32 ++++----
4 files changed, 139 insertions(+), 120 deletions(-)
diff --git a/src/api/backend.ts b/src/api/backend.ts
index cdc931c..b376423 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,6 +1,5 @@
const BASE_URL = "http://127.0.0.1:8000"; // API URL for Python backend
-
// Fetch detected smells for a given file
export async function fetchSmells(filePath: string): Promise {
const url = `${BASE_URL}/smells?file_path=${encodeURIComponent(filePath)}`;
@@ -9,8 +8,8 @@ export async function fetchSmells(filePath: string): Promise {
if (!response.ok) {
throw new Error(`Error fetching smells: ${response.statusText}`);
}
- const smellsList = await response.json();
- return smellsList as Smell[];
+ const smellsList = (await response.json()) as Smell[];
+ return smellsList;
} catch (error) {
console.error("Error in getSmells:", error);
throw error;
@@ -18,11 +17,10 @@ export async function fetchSmells(filePath: string): Promise {
}
// Request refactoring for a specific smell
-export async function refactorSmell(filePath: string, smell: Smell): Promise<{
- refactoredCode: string;
- energyDifference: number;
- updatedSmells: Smell[];
-}> {
+export async function refactorSmell(
+ filePath: string,
+ smell: Smell
+): Promise {
const url = `${BASE_URL}/refactor`;
const payload = {
file_path: filePath,
@@ -31,7 +29,7 @@ export async function refactorSmell(filePath: string, smell: Smell): Promise<{
try {
const response = await fetch(url, {
- method: "POST",
+ method: "GET",
headers: {
"Content-Type": "application/json",
},
@@ -42,12 +40,8 @@ export async function refactorSmell(filePath: string, smell: Smell): Promise<{
throw new Error(`Error refactoring smell: ${response.statusText}`);
}
- const refactorResult = await response.json();
- return refactorResult as {
- refactoredCode: string;
- energyDifference: number;
- updatedSmells: Smell[];
- };
+ const refactorResult = (await response.json()) as RefactorOutput;
+ return refactorResult;
} catch (error) {
console.error("Error in refactorSmell:", error);
throw error;
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index cee3211..008ed18 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -4,74 +4,103 @@ import { getEditorAndFilePath } from "../utils/editorUtils";
import { FileHighlighter } from "../ui/fileHighlighter";
import { Smell } from "../types";
import { fetchSmells, refactorSmell } from "../api/backend";
+import * as fs from "fs";
-
-async function refactorLine(smell: Smell, filePath: string, context: vscode.ExtensionContext){
- try {
- const refactorResult = await refactorSmell(filePath, smell);
- return refactorResult;
- } catch (error) {
- console.error("Error refactoring smell:", error);
- vscode.window.showErrorMessage(`Eco: Error refactoring smell: ${error}`);
- return;
- }
+async function refactorLine(
+ smell: Smell,
+ filePath: string,
+ context: vscode.ExtensionContext
+) {
+ try {
+ const refactorResult = await refactorSmell(filePath, smell);
+ return refactorResult;
+ } catch (error) {
+ console.error("Error refactoring smell:", error);
+ vscode.window.showErrorMessage(`Eco: Error refactoring smell: ${error}`);
+ return;
+ }
}
export async function refactorSelectedSmell(context: vscode.ExtensionContext) {
- const {editor, filePath} = getEditorAndFilePath();
+ const { editor, filePath } = getEditorAndFilePath();
- if (!editor) {
- vscode.window.showErrorMessage("Eco: Unable to proceed as no active editor found.");
- console.log("No active editor found to refactor smell. Returning back.");
- return;
- }
- if (!filePath) {
- vscode.window.showErrorMessage("Eco: Unable to proceed as active editor does not have a valid file path.");
- console.log("No valid file path found to refactor smell. Returning back.");
- return;
- }
+ if (!editor) {
+ vscode.window.showErrorMessage(
+ "Eco: Unable to proceed as no active editor found."
+ );
+ console.log("No active editor found to refactor smell. Returning back.");
+ return;
+ }
+ if (!filePath) {
+ vscode.window.showErrorMessage(
+ "Eco: Unable to proceed as active editor does not have a valid file path."
+ );
+ console.log("No valid file path found to refactor smell. Returning back.");
+ return;
+ }
- // only account for one selection to be refactored for now
- const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
+ // only account for one selection to be refactored for now
+ const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
- const smellsData = await fetchSmells(filePath);
- if (!smellsData || smellsData.length === 0) {
- vscode.window.showErrorMessage("Eco: No smells detected in the file for refactoring.");
- console.log("No smells found in the file for refactoring.");
- return;
- }
+ const smellsData = await fetchSmells(filePath);
+ if (!smellsData || smellsData.length === 0) {
+ vscode.window.showErrorMessage(
+ "Eco: No smells detected in the file for refactoring."
+ );
+ console.log("No smells found in the file for refactoring.");
+ return;
+ }
- const matchingSmells = smellsData.filter((smell: Smell) => {
- return selectedLine === smell.line;
- });
+ const matchingSmells = smellsData.filter((smell: Smell) => {
+ return selectedLine === smell.occurrences[0].line;
+ });
- if (matchingSmells.length === 0) {
- vscode.window.showInformationMessage("Eco: Selected line(s) does not include a refactorable code pattern. Please switch to a line with highlighted code smell.");
- return;
- }
+ if (matchingSmells.length === 0) {
+ vscode.window.showInformationMessage(
+ "Eco: Selected line(s) does not include a refactorable code pattern. Please switch to a line with highlighted code smell."
+ );
+ return;
+ }
- vscode.window.showInformationMessage('Eco: Refactoring smell on selected line.');
- console.log("Detecting smells in detectSmells on selected line");
+ vscode.window.showInformationMessage(
+ "Eco: Refactoring smell on selected line."
+ );
+ console.log("Detecting smells in detectSmells on selected line");
- //refactor the first found smell
- //TODO UI that allows users to choose the smell to refactor
- const refactorResult = await refactorLine(matchingSmells[0], filePath, context);
- if (!refactorResult) {
- vscode.window.showErrorMessage("Eco: Refactoring failed. See console for details.");
- return;
- }
- const refactoredCode = refactorResult.refactoredCode;
- const energyDifference = refactorResult.energyDifference;
- const updatedSmells = refactorResult.updatedSmells;
+ //refactor the first found smell
+ //TODO UI that allows users to choose the smell to refactor
+ const refactorResult = await refactorLine(
+ matchingSmells[0],
+ filePath,
+ context
+ );
+ if (!refactorResult) {
+ vscode.window.showErrorMessage(
+ "Eco: Refactoring failed. See console for details."
+ );
+ return;
+ }
+ const { refactoredData, updatedSmells } = refactorResult;
- await RefactorManager.previewRefactor(editor, refactoredCode);
+ // Did not test this yet, but if it works need to change so that all modified files are displayed
+ // only shows the file where the smell was found
+ fs.readFile(refactoredData.targetFile, async (err, data) => {
+ if (err) {
+ throw err;
+ }
+ await RefactorManager.previewRefactor(editor, data.toString("utf8"));
vscode.window.showInformationMessage(
- `Eco: Refactoring completed. Energy difference: ${energyDifference.toFixed(4)}`
+ `Eco: Refactoring completed. Energy difference: ${refactoredData.energySaved.toFixed(
+ 4
+ )}`
);
-
- if (updatedSmells) {
- FileHighlighter.highlightSmells(editor, updatedSmells);
- } else {
- vscode.window.showWarningMessage("Eco: No updated smells detected after refactoring.");
- }
+ });
+
+ if (updatedSmells.length) {
+ FileHighlighter.highlightSmells(editor, updatedSmells);
+ } else {
+ vscode.window.showWarningMessage(
+ "Eco: No updated smells detected after refactoring."
+ );
+ }
}
diff --git a/src/global.d.ts b/src/global.d.ts
index 3a437e4..fb619c0 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -4,36 +4,36 @@ export {};
export {};
declare global {
- // Define your global types here
- interface Occurrence {
- line: number;
- column: number;
- call_string: string;
- }
-
- interface Smell {
- type: string; // Type of the smell (e.g., "performance", "convention")
- symbol: string; // Symbolic identifier for the smell (e.g., "cached-repeated-calls")
- message: string; // Detailed description of the smell
- messageId: string; // Unique ID for the smell
- line?: number; // Line number where the smell is detected
- column?: number; // Column offset where the smell starts
- endLine?: number; // Optional: Ending line for multiline smells
- endColumn?: number; // Optional: Ending column for multiline smells
- confidence: string; // Confidence level (e.g., "HIGH", "MEDIUM")
- path?: string; // Relative file path
- absolutePath?: string; // Absolute file pat.
- module?: string; // Module name
- obj?: string; // Object name associated with the smell (if applicable)
- repetitions?: number; // Optional: Number of repeated occurrences
- occurrences?: Occurrence[]; // Optional: List of occurrences for repeated calls
- }
-
-
+ // Define your global types here
+ interface Occurrence {
+ line: number;
+ endLine?: number;
+ column: number;
+ endColumn?: number;
+ }
- interface RefactorOutput {
- refactored_code: string; // Refactored code as a string
- energy_difference?: number; // Optional: energy difference (if provided)
- updated_smells?: any[]; // Optional: updated smells (if provided)
- }
+ interface Smell {
+ type: string; // Type of the smell (e.g., "performance", "convention")
+ symbol: string; // Symbolic identifier for the smell (e.g., "cached-repeated-calls")
+ message: string; // Detailed description of the smell
+ messageId: string; // Unique ID for the smell
+ confidence: string; // Confidence level (e.g., "HIGH", "MEDIUM")
+ path: string; // Optional: absolute file path
+ module: string; // Optional: Module name
+ obj: string; // Optional: Object name associated with the smell (if applicable)
+ occurrences: Occurrence[]; // Optional: List of occurrences for repeated calls
+ additionalInfo?: any;
+ }
+
+ interface RefactoredData {
+ temp_dir: string;
+ targetFile: string;
+ energySaved: number;
+ refactoredFiles: string[];
+ }
+
+ interface RefactorOutput {
+ refactoredData: RefactoredData; // Refactored code as a string
+ updatedSmells: Smell[]; //
+ }
}
diff --git a/src/types.ts b/src/types.ts
index 8785364..b0cb545 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,23 +1,19 @@
interface Occurrence {
- line: number;
- column: number;
- call_string: string;
+ line: number;
+ endLine?: number;
+ column: number;
+ endColumn?: number;
}
export interface Smell {
- type: string; // Type of the smell (e.g., "performance", "convention")
- symbol: string; // Symbolic identifier for the smell (e.g., "cached-repeated-calls")
- message: string; // Detailed description of the smell
- messageId: string; // Unique ID for the smell
- line?: number; // Optional: Line number where the smell is detected
- column?: number; // Optional: Column offset where the smell starts
- endLine?: number; // Optional: Ending line for multiline smells
- endColumn?: number; // Optional: Ending column for multiline smells
- confidence: string; // Confidence level (e.g., "HIGH", "MEDIUM")
- path?: string; // Optional: Relative file path
- absolutePath?: string; // Optional: Absolute file pat.
- module?: string; // Optional: Module name
- obj?: string; // Optional: Object name associated with the smell (if applicable)
- repetitions?: number; // Optional: Number of repeated occurrences
- occurrences?: Occurrence[]; // Optional: List of occurrences for repeated calls
+ type: string; // Type of the smell (e.g., "performance", "convention")
+ symbol: string; // Symbolic identifier for the smell (e.g., "cached-repeated-calls")
+ message: string; // Detailed description of the smell
+ messageId: string; // Unique ID for the smell
+ confidence: string; // Confidence level (e.g., "HIGH", "MEDIUM")
+ path: string; // Optional: absolute file path
+ module: string; // Optional: Module name
+ obj: string; // Optional: Object name associated with the smell (if applicable)
+ occurrences: Occurrence[]; // Optional: List of occurrences for repeated calls
+ additionalInfo?: any;
}
From 2241185995eb251b27fdcdd956d59d88265b0964 Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Wed, 29 Jan 2025 14:03:30 -0500
Subject: [PATCH 02/83] updated readme with instructions for now
---
README.md | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index cb7d990..9f7fad6 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,12 @@
-VS Code Plugin for Source Code Optimizer
\ No newline at end of file
+VS Code Plugin for Source Code Optimizer
+
+1. clone this repo
+2. open terminal and write:
+ npm install
+ npm run compile
+3. open another vs code window with the ecooptimzer repo
+4. start venv in the ecooptimizer repo
+5. run "python -m ecoopitmizer.api.main" in terminal to start the developement server manually
+6. come back to this repo, go to run and debug
+7. run extension (should open a new vs code window so open the repo u want in this)
+8. in the vscode search bar type ">eco: detect smells" and run it
From bf49447bcd424b1fcefb7880b84b653df829aba8 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 27 Jan 2025 20:50:35 -0500
Subject: [PATCH 03/83] fixed issue off highlighting entire line
fixes ssm-lab/capstone--sco-vs-code-plugin#351
---
.prettierrc | 5 +++
src/api/backend.ts | 14 ++++-----
src/commands/refactorSmell.ts | 42 +++++++++++++-------------
src/global.d.ts | 2 +-
src/types.ts | 2 +-
src/ui/fileHighlighter.ts | 57 +++++++++++++++++++++--------------
6 files changed, 70 insertions(+), 52 deletions(-)
create mode 100644 .prettierrc
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..aa566c4
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "singleQuote": true,
+ "endOfLine": "auto",
+ "trailingComma": "none"
+}
diff --git a/src/api/backend.ts b/src/api/backend.ts
index b376423..64f54f9 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,4 +1,4 @@
-const BASE_URL = "http://127.0.0.1:8000"; // API URL for Python backend
+const BASE_URL = 'http://127.0.0.1:8000'; // API URL for Python backend
// Fetch detected smells for a given file
export async function fetchSmells(filePath: string): Promise {
@@ -11,7 +11,7 @@ export async function fetchSmells(filePath: string): Promise {
const smellsList = (await response.json()) as Smell[];
return smellsList;
} catch (error) {
- console.error("Error in getSmells:", error);
+ console.error('Error in getSmells:', error);
throw error;
}
}
@@ -24,16 +24,16 @@ export async function refactorSmell(
const url = `${BASE_URL}/refactor`;
const payload = {
file_path: filePath,
- smell: smell,
+ smell: smell
};
try {
const response = await fetch(url, {
- method: "GET",
+ method: 'POST',
headers: {
- "Content-Type": "application/json",
+ 'Content-Type': 'application/json'
},
- body: JSON.stringify(payload),
+ body: JSON.stringify(payload)
});
if (!response.ok) {
@@ -43,7 +43,7 @@ export async function refactorSmell(
const refactorResult = (await response.json()) as RefactorOutput;
return refactorResult;
} catch (error) {
- console.error("Error in refactorSmell:", error);
+ console.error('Error in refactorSmell:', error);
throw error;
}
}
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 008ed18..9d94a25 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -1,10 +1,10 @@
-import * as vscode from "vscode";
-import { RefactorManager } from "../ui/refactorManager";
-import { getEditorAndFilePath } from "../utils/editorUtils";
-import { FileHighlighter } from "../ui/fileHighlighter";
-import { Smell } from "../types";
-import { fetchSmells, refactorSmell } from "../api/backend";
-import * as fs from "fs";
+import * as vscode from 'vscode';
+import { RefactorManager } from '../ui/refactorManager';
+import { getEditorAndFilePath } from '../utils/editorUtils';
+import { FileHighlighter } from '../ui/fileHighlighter';
+import { Smell } from '../types';
+import { fetchSmells, refactorSmell } from '../api/backend';
+import * as fs from 'fs';
async function refactorLine(
smell: Smell,
@@ -15,7 +15,7 @@ async function refactorLine(
const refactorResult = await refactorSmell(filePath, smell);
return refactorResult;
} catch (error) {
- console.error("Error refactoring smell:", error);
+ console.error('Error refactoring smell:', error);
vscode.window.showErrorMessage(`Eco: Error refactoring smell: ${error}`);
return;
}
@@ -26,16 +26,16 @@ export async function refactorSelectedSmell(context: vscode.ExtensionContext) {
if (!editor) {
vscode.window.showErrorMessage(
- "Eco: Unable to proceed as no active editor found."
+ 'Eco: Unable to proceed as no active editor found.'
);
- console.log("No active editor found to refactor smell. Returning back.");
+ console.log('No active editor found to refactor smell. Returning back.');
return;
}
if (!filePath) {
vscode.window.showErrorMessage(
- "Eco: Unable to proceed as active editor does not have a valid file path."
+ 'Eco: Unable to proceed as active editor does not have a valid file path.'
);
- console.log("No valid file path found to refactor smell. Returning back.");
+ console.log('No valid file path found to refactor smell. Returning back.');
return;
}
@@ -45,27 +45,27 @@ export async function refactorSelectedSmell(context: vscode.ExtensionContext) {
const smellsData = await fetchSmells(filePath);
if (!smellsData || smellsData.length === 0) {
vscode.window.showErrorMessage(
- "Eco: No smells detected in the file for refactoring."
+ 'Eco: No smells detected in the file for refactoring.'
);
- console.log("No smells found in the file for refactoring.");
+ console.log('No smells found in the file for refactoring.');
return;
}
const matchingSmells = smellsData.filter((smell: Smell) => {
- return selectedLine === smell.occurrences[0].line;
+ return selectedLine === smell.occurences[0].line;
});
if (matchingSmells.length === 0) {
vscode.window.showInformationMessage(
- "Eco: Selected line(s) does not include a refactorable code pattern. Please switch to a line with highlighted code smell."
+ 'Eco: Selected line(s) does not include a refactorable code pattern. Please switch to a line with highlighted code smell.'
);
return;
}
vscode.window.showInformationMessage(
- "Eco: Refactoring smell on selected line."
+ 'Eco: Refactoring smell on selected line.'
);
- console.log("Detecting smells in detectSmells on selected line");
+ console.log('Detecting smells in detectSmells on selected line');
//refactor the first found smell
//TODO UI that allows users to choose the smell to refactor
@@ -76,7 +76,7 @@ export async function refactorSelectedSmell(context: vscode.ExtensionContext) {
);
if (!refactorResult) {
vscode.window.showErrorMessage(
- "Eco: Refactoring failed. See console for details."
+ 'Eco: Refactoring failed. See console for details.'
);
return;
}
@@ -88,7 +88,7 @@ export async function refactorSelectedSmell(context: vscode.ExtensionContext) {
if (err) {
throw err;
}
- await RefactorManager.previewRefactor(editor, data.toString("utf8"));
+ await RefactorManager.previewRefactor(editor, data.toString('utf8'));
vscode.window.showInformationMessage(
`Eco: Refactoring completed. Energy difference: ${refactoredData.energySaved.toFixed(
4
@@ -100,7 +100,7 @@ export async function refactorSelectedSmell(context: vscode.ExtensionContext) {
FileHighlighter.highlightSmells(editor, updatedSmells);
} else {
vscode.window.showWarningMessage(
- "Eco: No updated smells detected after refactoring."
+ 'Eco: No updated smells detected after refactoring.'
);
}
}
diff --git a/src/global.d.ts b/src/global.d.ts
index fb619c0..44abb2a 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -21,7 +21,7 @@ declare global {
path: string; // Optional: absolute file path
module: string; // Optional: Module name
obj: string; // Optional: Object name associated with the smell (if applicable)
- occurrences: Occurrence[]; // Optional: List of occurrences for repeated calls
+ occurences: Occurrence[]; // Optional: List of occurrences for repeated calls
additionalInfo?: any;
}
diff --git a/src/types.ts b/src/types.ts
index b0cb545..65cb228 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -14,6 +14,6 @@ export interface Smell {
path: string; // Optional: absolute file path
module: string; // Optional: Module name
obj: string; // Optional: Object name associated with the smell (if applicable)
- occurrences: Occurrence[]; // Optional: List of occurrences for repeated calls
+ occurences: Occurrence[]; // Optional: List of occurrences for repeated calls
additionalInfo?: any;
}
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index f8afd5b..6956708 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -1,33 +1,46 @@
-import * as vscode from "vscode";
+import * as vscode from 'vscode';
+import { getEditor } from '../utils/editorUtils';
export class FileHighlighter {
- static highlightSmells(editor: vscode.TextEditor, smells: any[]) {
-
+ static highlightSmells(editor: vscode.TextEditor, smells: Smell[]) {
+ const yellowUnderline = vscode.window.createTextEditorDecorationType({
+ textDecoration: 'underline yellow'
+ });
- const yellowUnderline = vscode.window.createTextEditorDecorationType({
- textDecoration: 'underline yellow',
- });
-
+ const decorations: vscode.DecorationOptions[] = smells
+ .filter((smell: Smell) =>
+ smell.occurences.every((occurrence: { line: number }) =>
+ isValidLine(occurrence.line)
+ )
+ )
+ .flatMap((smell: any) => {
+ return smell.occurences.map((occurrence: { line: number }) => {
+ const line = occurrence.line - 1; // convert to zero-based line index for VS editor
+
+ const line_text = editor.document.lineAt(line).text;
+ const line_length = line_text.length;
+ const indexStart = line_length - line_text.trimStart().length;
+ const indexEnd = line_text.trimEnd().length;
- const decorations: vscode.DecorationOptions[] = smells.filter((smell: Smell) => isValidLine(smell.line)).map((smell: any) => {
- const line = smell.line - 1; // convert to zero-based line index for VS editor
- const range = new vscode.Range(line, 0, line, editor.document.lineAt(line).text.length);
+ const range = new vscode.Range(line, indexStart, line, indexEnd);
- return { range, hoverMessage: `Smell: ${smell.message}` }; // option to hover over and read smell details
+ return { range, hoverMessage: `Smell: ${smell.message}` }; // option to hover over and read smell details
});
+ });
- editor.setDecorations(yellowUnderline, decorations);
- console.log("Updated smell line highlights");
- }
+ editor.setDecorations(yellowUnderline, decorations);
+ console.log('Updated smell line highlights');
+ }
}
function isValidLine(line: any): boolean {
- return (
- line !== undefined &&
- line !== null &&
- typeof line === 'number' &&
- Number.isFinite(line) &&
- line > 0 &&
- Number.isInteger(line)
- );
+ return (
+ line !== undefined &&
+ line !== null &&
+ typeof line === 'number' &&
+ Number.isFinite(line) &&
+ line > 0 &&
+ Number.isInteger(line) &&
+ line <= getEditor()!.document.lineCount
+ );
}
From 9b10f8221cbd16f5ca5386312d4bcb68e1b2c63e Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Wed, 29 Jan 2025 18:04:44 -0500
Subject: [PATCH 04/83] Updated smell detection functionality
fixes ssm-lab/capstone--sco-vs-code-plugin#351
---
.env | 2 +
.vscode/settings.json | 5 +-
package-lock.json | 341 ++++++++++++++++++++++++---------
package.json | 26 ++-
src/commands/detectSmells.ts | 144 ++++++++++----
src/commands/refactorSmell.ts | 7 +-
src/context/contextManager.ts | 33 ++++
src/extension.ts | 167 +++++++++++++---
src/ui/fileHighlighter.ts | 28 ++-
src/ui/lineSelectionManager.ts | 95 +++++++++
src/utils/envConfig.ts | 13 ++
webpack.config.js | 12 +-
12 files changed, 703 insertions(+), 170 deletions(-)
create mode 100644 .env
create mode 100644 src/context/contextManager.ts
create mode 100644 src/ui/lineSelectionManager.ts
create mode 100644 src/utils/envConfig.ts
diff --git a/.env b/.env
new file mode 100644
index 0000000..2b85682
--- /dev/null
+++ b/.env
@@ -0,0 +1,2 @@
+SMELL_MAP_KEY='workspaceSmells'
+FILE_CHANGES_KEY='lastSavedHashes'
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8a453d0..7ad2f2c 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -9,5 +9,8 @@
"dist": true
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
- "typescript.tsc.autoDetect": "off"
+ "typescript.tsc.autoDetect": "off",
+ "cSpell.words": [
+ "occurences"
+ ]
}
diff --git a/package-lock.json b/package-lock.json
index a87bfd8..ab02400 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,10 @@
"": {
"name": "ecooptimizer-vs-code-plugin",
"version": "0.0.1",
+ "dependencies": {
+ "dotenv": "^16.4.7",
+ "dotenv-webpack": "^8.1.0"
+ },
"devDependencies": {
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
@@ -15,7 +19,9 @@
"@typescript-eslint/parser": "^8.17.0",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
+ "css-loader": "^7.1.2",
"eslint": "^9.16.0",
+ "style-loader": "^4.0.0",
"ts-loader": "^9.5.1",
"typescript": "^5.7.2",
"webpack": "^5.95.0",
@@ -303,7 +309,6 @@
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
@@ -318,7 +323,6 @@
"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,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -328,7 +332,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -338,7 +341,6 @@
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -349,14 +351,12 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
- "dev": true,
"license": "MIT"
},
"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,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -416,7 +416,6 @@
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*",
@@ -427,7 +426,6 @@
"version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint": "*",
@@ -438,7 +436,6 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
- "dev": true,
"license": "MIT"
},
"node_modules/@types/istanbul-lib-coverage": {
@@ -452,7 +449,6 @@
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true,
"license": "MIT"
},
"node_modules/@types/mocha": {
@@ -466,7 +462,6 @@
"version": "20.17.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz",
"integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
@@ -717,7 +712,6 @@
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/helper-numbers": "1.13.2",
@@ -728,28 +722,24 @@
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
- "dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-api-error": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
- "dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
@@ -761,14 +751,12 @@
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
- "dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
@@ -781,7 +769,6 @@
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
@@ -791,7 +778,6 @@
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
- "dev": true,
"license": "Apache-2.0",
"dependencies": {
"@xtuc/long": "4.2.2"
@@ -801,14 +787,12 @@
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
@@ -825,7 +809,6 @@
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
@@ -839,7 +822,6 @@
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
@@ -852,7 +834,6 @@
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
@@ -867,7 +848,6 @@
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
@@ -925,21 +905,18 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
- "dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@xtuc/long": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
- "dev": true,
"license": "Apache-2.0"
},
"node_modules/acorn": {
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
- "dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -972,7 +949,6 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
@@ -989,7 +965,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^8.0.0"
@@ -1007,7 +982,6 @@
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -1024,14 +998,12 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true,
"license": "MIT"
},
"node_modules/ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
- "dev": true,
"license": "MIT",
"peerDependencies": {
"ajv": "^6.9.1"
@@ -1199,7 +1171,6 @@
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -1257,7 +1228,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/c8": {
@@ -1313,7 +1283,6 @@
"version": "1.0.30001692",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
"integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -1389,7 +1358,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
"integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0"
@@ -1548,7 +1516,6 @@
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/concat-map": {
@@ -1587,6 +1554,53 @@
"node": ">= 8"
}
},
+ "node_modules/css-loader": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz",
+ "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.1.0",
+ "postcss": "^8.4.33",
+ "postcss-modules-extract-imports": "^3.1.0",
+ "postcss-modules-local-by-default": "^4.0.5",
+ "postcss-modules-scope": "^3.2.0",
+ "postcss-modules-values": "^4.0.0",
+ "postcss-value-parser": "^4.2.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "@rspack/core": "0.x || 1.x",
+ "webpack": "^5.27.0"
+ },
+ "peerDependenciesMeta": {
+ "@rspack/core": {
+ "optional": true
+ },
+ "webpack": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
@@ -1635,6 +1649,47 @@
"node": ">=0.3.1"
}
},
+ "node_modules/dotenv": {
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dotenv-defaults": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz",
+ "integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==",
+ "dependencies": {
+ "dotenv": "^8.2.0"
+ }
+ },
+ "node_modules/dotenv-defaults/node_modules/dotenv": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
+ "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/dotenv-webpack": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.1.0.tgz",
+ "integrity": "sha512-owK1JcsPkIobeqjVrk6h7jPED/W6ZpdFsMPR+5ursB7/SdgDyO+VzAU+szK8C8u3qUhtENyYnj8eyXMR5kkGag==",
+ "dependencies": {
+ "dotenv-defaults": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "webpack": "^4 || ^5"
+ }
+ },
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -1646,7 +1701,6 @@
"version": "1.5.83",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz",
"integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==",
- "dev": true,
"license": "ISC"
},
"node_modules/emoji-regex": {
@@ -1660,7 +1714,6 @@
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
"integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
@@ -1687,14 +1740,12 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
"integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -1901,7 +1952,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"estraverse": "^5.2.0"
@@ -1914,7 +1964,6 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@@ -1934,7 +1983,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.8.x"
@@ -1944,7 +1992,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true,
"license": "MIT"
},
"node_modules/fast-glob": {
@@ -1968,7 +2015,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true,
"license": "MIT"
},
"node_modules/fast-levenshtein": {
@@ -1982,7 +2028,6 @@
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz",
"integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -2186,7 +2231,6 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
- "dev": true,
"license": "BSD-2-Clause"
},
"node_modules/globals": {
@@ -2206,7 +2250,6 @@
"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==",
- "dev": true,
"license": "ISC"
},
"node_modules/graphemer": {
@@ -2220,7 +2263,6 @@
"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"
@@ -2284,6 +2326,18 @@
"node": ">= 14"
}
},
+ "node_modules/icss-utils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "dev": true,
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -2615,7 +2669,6 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*",
@@ -2630,7 +2683,6 @@
"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"
@@ -2666,14 +2718,12 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
- "dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true,
"license": "MIT"
},
"node_modules/json-stable-stringify-without-jsonify": {
@@ -2744,7 +2794,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.11.5"
@@ -2817,7 +2866,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
- "dev": true,
"license": "MIT"
},
"node_modules/merge2": {
@@ -2848,7 +2896,6 @@
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -2858,7 +2905,6 @@
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
@@ -3100,6 +3146,24 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/nanoid": {
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -3111,14 +3175,12 @@
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
- "dev": true,
"license": "MIT"
},
"node_modules/node-releases": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
- "dev": true,
"license": "MIT"
},
"node_modules/normalize-path": {
@@ -3394,7 +3456,6 @@
"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": {
@@ -3479,6 +3540,112 @@
"node": ">=8"
}
},
+ "node_modules/postcss": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
+ "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.8",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-modules-extract-imports": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
+ "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
+ "dev": true,
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-local-by-default": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz",
+ "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0",
+ "postcss-value-parser": "^4.1.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-scope": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz",
+ "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-values": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+ "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
+ "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -3500,7 +3667,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -3531,7 +3697,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "^5.1.0"
@@ -3593,7 +3758,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -3716,14 +3880,12 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true,
"license": "MIT"
},
"node_modules/schema-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.8",
@@ -3755,7 +3917,6 @@
"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"
@@ -3827,11 +3988,19 @@
"node": ">= 8"
}
},
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
@@ -3842,7 +4011,6 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -3991,6 +4159,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/style-loader": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz",
+ "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.27.0"
+ }
+ },
"node_modules/supports-color": {
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz",
@@ -4021,7 +4205,6 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -4031,7 +4214,6 @@
"version": "5.37.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz",
"integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==",
- "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
@@ -4050,7 +4232,6 @@
"version": "5.3.11",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz",
"integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.25",
@@ -4085,7 +4266,6 @@
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -4102,7 +4282,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
@@ -4115,14 +4294,12 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true,
"license": "MIT"
},
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
@@ -4277,14 +4454,12 @@
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
- "dev": true,
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
"integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -4315,7 +4490,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
@@ -4347,7 +4521,6 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"glob-to-regexp": "^0.4.1",
@@ -4361,7 +4534,6 @@
"version": "5.97.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint-scope": "^3.7.7",
@@ -4479,7 +4651,6 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=10.13.0"
@@ -4489,7 +4660,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
- "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
@@ -4503,7 +4673,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
diff --git a/package.json b/package.json
index 2e96184..9ea979f 100644
--- a/package.json
+++ b/package.json
@@ -27,11 +27,11 @@
}
],
"keybindings": [
- {
- "command": "ecooptimizer-vs-code-plugin.refactorSmell",
- "key": "ctrl+shift+r",
- "when": "editorTextFocus && resourceExtname == '.py'"
- }
+ {
+ "command": "ecooptimizer-vs-code-plugin.refactorSmell",
+ "key": "ctrl+shift+r",
+ "when": "editorTextFocus && resourceExtname == '.py'"
+ }
]
},
"scripts": {
@@ -46,17 +46,23 @@
"test": "vscode-test"
},
"devDependencies": {
- "@types/vscode": "^1.92.0",
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
+ "@types/vscode": "^1.92.0",
"@typescript-eslint/eslint-plugin": "^8.17.0",
"@typescript-eslint/parser": "^8.17.0",
+ "@vscode/test-cli": "^0.0.10",
+ "@vscode/test-electron": "^2.4.1",
+ "css-loader": "^7.1.2",
"eslint": "^9.16.0",
- "typescript": "^5.7.2",
+ "style-loader": "^4.0.0",
"ts-loader": "^9.5.1",
+ "typescript": "^5.7.2",
"webpack": "^5.95.0",
- "webpack-cli": "^5.1.4",
- "@vscode/test-cli": "^0.0.10",
- "@vscode/test-electron": "^2.4.1"
+ "webpack-cli": "^5.1.4"
+ },
+ "dependencies": {
+ "dotenv": "^16.4.7",
+ "dotenv-webpack": "^8.1.0"
}
}
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index b2e5929..5070176 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -1,53 +1,115 @@
-import * as vscode from "vscode";
-import { FileHighlighter } from "../ui/fileHighlighter";
-import { getEditorAndFilePath } from "../utils/editorUtils";
-import * as fs from "fs";
-import { Smell } from "../types";
-import { fetchSmells } from "../api/backend";
-
-export async function getSmells(filePath: string, context: vscode.ExtensionContext) {
- try {
- const smellsList : Smell[] = await fetchSmells(filePath);
- if (smellsList.length === 0) {
- throw new Error("Detected smells data is invalid or empty.");
- }
-
- return smellsList;
- } catch(error) {
- console.error("Error detecting smells:", error);
- vscode.window.showErrorMessage(`Eco: Error detecting smells: ${error}`);
- return;
+import * as vscode from 'vscode';
+import { FileHighlighter } from '../ui/fileHighlighter';
+import { getEditorAndFilePath } from '../utils/editorUtils';
+import * as fs from 'fs';
+import { Smell } from '../types';
+import { fetchSmells } from '../api/backend';
+import { ContextManager } from '../context/contextManager';
+import { envConfig } from '../utils/envConfig';
+
+export interface SmellDetectRecord {
+ hash: string;
+ smells: Smell[];
+}
+
+export async function getSmells(
+ filePath: string,
+ contextManager: ContextManager
+) {
+ try {
+ const smellsList: Smell[] = await fetchSmells(filePath);
+ if (smellsList.length === 0) {
+ throw new Error('Detected smells data is invalid or empty.');
}
+
+ return smellsList;
+ } catch (error) {
+ console.error('Error detecting smells:', error);
+ vscode.window.showErrorMessage(`Eco: Error detecting smells: ${error}`);
+ return;
+ }
}
-export async function detectSmells(context: vscode.ExtensionContext){
- const {editor, filePath} = getEditorAndFilePath();
+export async function detectSmells(contextManager: ContextManager) {
+ const { editor, filePath } = getEditorAndFilePath();
- if (!editor) {
- vscode.window.showErrorMessage("Eco: Unable to proceed as no active editor found.");
- console.error("No active editor found to detect smells. Returning back.");
- return;
- }
- if (!filePath) {
- vscode.window.showErrorMessage("Eco: Unable to proceed as active editor does not have a valid file path.");
- console.error("No valid file path found to detect smells. Returning back.");
- return;
+ if (!editor) {
+ vscode.window.showErrorMessage(
+ 'Eco: Unable to proceed as no active editor found.'
+ );
+ console.error('No active editor found to detect smells. Returning back.');
+ return;
+ }
+ if (!filePath) {
+ vscode.window.showErrorMessage(
+ 'Eco: Unable to proceed as active editor does not have a valid file path.'
+ );
+ console.error('No valid file path found to detect smells. Returning back.');
+ return;
+ }
+
+ vscode.window.showInformationMessage('Eco: Detecting smells...');
+ console.log('Detecting smells in detectSmells');
+
+ let smellsData: Smell[] | undefined;
+
+ // Get the stored smells and current file hash
+ const allSmells = contextManager.getWorkspaceData(
+ envConfig.SMELL_MAP_KEY!
+ ) as Record;
+
+ const fileSmells = allSmells[filePath];
+
+ const currentFileHash = contextManager.getWorkspaceData(
+ envConfig.FILE_CHANGES_KEY!
+ )[filePath];
+
+ // Function to handle the smells data retrieval and updating
+ async function fetchAndStoreSmells(): Promise {
+ smellsData = await getSmells(filePath!, contextManager);
+
+ if (!smellsData) {
+ console.log('No valid smells data found. Returning.');
+ vscode.window.showErrorMessage(
+ 'Eco: No smells are present in current file.'
+ );
+ return undefined; // Indicate failure to fetch smells
}
- vscode.window.showInformationMessage("Eco: Detecting smells...");
- console.log("Detecting smells in detectSmells");
-
- const smellsData = await getSmells(filePath, context);
+ allSmells[filePath!] = {
+ hash: currentFileHash,
+ smells: smellsData
+ };
+ contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, allSmells);
+
+ return smellsData; // Successfully fetched and stored smells
+ }
- if (!smellsData){
- console.log("No valid smells data found. Returning.");
- vscode.window.showErrorMessage("Eco: No smells are present in current file.");
+ if (fileSmells) {
+ if (currentFileHash === fileSmells.hash) {
+ smellsData = fileSmells.smells;
+ } else {
+ smellsData = await fetchAndStoreSmells();
+ if (!smellsData) {
return;
+ }
}
+ } else {
+ smellsData = await fetchAndStoreSmells();
+ if (!smellsData) {
+ return;
+ }
+ }
+
+ console.log('Saving smells to workspace data.');
- console.log("Detected smells data: ", smellsData);
- vscode.window.showInformationMessage(`Eco: Detected ${smellsData.length} smells in the file.`);
+ console.log('Detected smells data: ', smellsData);
+ vscode.window.showInformationMessage(
+ `Eco: Detected ${smellsData.length} smells in the file.`
+ );
- FileHighlighter.highlightSmells(editor, smellsData);
- vscode.window.showInformationMessage("Eco: Detected code smells have been highlighted.");
+ FileHighlighter.highlightSmells(editor, smellsData);
+ vscode.window.showInformationMessage(
+ 'Eco: Detected code smells have been highlighted.'
+ );
}
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 9d94a25..1f95c58 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -5,11 +5,12 @@ import { FileHighlighter } from '../ui/fileHighlighter';
import { Smell } from '../types';
import { fetchSmells, refactorSmell } from '../api/backend';
import * as fs from 'fs';
+import { ContextManager } from '../context/contextManager';
async function refactorLine(
smell: Smell,
filePath: string,
- context: vscode.ExtensionContext
+ contextManager: ContextManager
) {
try {
const refactorResult = await refactorSmell(filePath, smell);
@@ -21,7 +22,7 @@ async function refactorLine(
}
}
-export async function refactorSelectedSmell(context: vscode.ExtensionContext) {
+export async function refactorSelectedSmell(contextManager: ContextManager) {
const { editor, filePath } = getEditorAndFilePath();
if (!editor) {
@@ -72,7 +73,7 @@ export async function refactorSelectedSmell(context: vscode.ExtensionContext) {
const refactorResult = await refactorLine(
matchingSmells[0],
filePath,
- context
+ contextManager
);
if (!refactorResult) {
vscode.window.showErrorMessage(
diff --git a/src/context/contextManager.ts b/src/context/contextManager.ts
new file mode 100644
index 0000000..58819a6
--- /dev/null
+++ b/src/context/contextManager.ts
@@ -0,0 +1,33 @@
+import * as vscode from 'vscode';
+
+export class ContextManager {
+ public context: vscode.ExtensionContext;
+
+ constructor(context: vscode.ExtensionContext) {
+ this.context = context;
+ }
+
+ // Global state example
+ public getGlobalData(
+ key: string,
+ defaultVal: any = undefined
+ ): any | undefined {
+ return this.context.globalState.get(key, defaultVal);
+ }
+
+ public setGlobalData(key: string, value: any): Thenable {
+ return this.context.globalState.update(key, value);
+ }
+
+ // Workspace state example
+ public getWorkspaceData(
+ key: string,
+ defaultVal: any = undefined
+ ): any | undefined {
+ return this.context.workspaceState.get(key, defaultVal);
+ }
+
+ public setWorkspaceData(key: string, value: any): Thenable {
+ return this.context.workspaceState.update(key, value);
+ }
+}
diff --git a/src/extension.ts b/src/extension.ts
index c6ff254..872c508 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,33 +1,156 @@
-import * as vscode from "vscode";
-import { detectSmells } from "./commands/detectSmells";
-import { refactorSelectedSmell } from "./commands/refactorSmell";
-import { getEditor } from "./utils/editorUtils";
+import { envConfig } from './utils/envConfig';
+
+import * as vscode from 'vscode';
+import { detectSmells } from './commands/detectSmells';
+import { refactorSelectedSmell } from './commands/refactorSmell';
+import { LineSelectionManager } from './ui/lineSelectionManager';
+import * as crypto from 'crypto';
+import { ContextManager } from './context/contextManager';
interface Smell {
- line: number; // Known attribute
- [key: string]: any; // Index signature for unknown properties
+ line: number; // Known attribute
+ [key: string]: any; // Index signature for unknown properties
}
export function activate(context: vscode.ExtensionContext) {
- console.log("Refactor Plugin activated");
+ console.log('Refactor Plugin activated');
+
+ const contextManager = new ContextManager(context);
+
+ // ===============================================================
+ // INITIALIZE WORKSPACE DATA
+ // ===============================================================
- // Register Detect Smells Command
- let detectSmellsCmd = vscode.commands.registerCommand("ecooptimizer-vs-code-plugin.detectSmells", async () => {
- console.log("Command detectSmells triggered");
- detectSmells(context);
- }
+ let allDetectedSmells = contextManager.getWorkspaceData(
+ envConfig.SMELL_MAP_KEY!
+ );
+ let fileHashes = contextManager.getWorkspaceData(envConfig.FILE_CHANGES_KEY!);
+
+ if (!allDetectedSmells) {
+ allDetectedSmells = {};
+ contextManager.setWorkspaceData(
+ envConfig.SMELL_MAP_KEY!,
+ allDetectedSmells
);
- context.subscriptions.push(detectSmellsCmd);
-
- // Register Refactor Smell Command
- let refactorSmellCmd = vscode.commands.registerCommand("ecooptimizer-vs-code-plugin.refactorSmell", () => {
- console.log("Command refactorSmells triggered");
- vscode.window.showInformationMessage("Eco: Detecting smells...");
- refactorSelectedSmell(context);
- });
- context.subscriptions.push(refactorSmellCmd);
+ }
+
+ if (!fileHashes) {
+ fileHashes = {};
+ contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, fileHashes);
+ }
+
+ console.log(
+ `Smell detection map: ${contextManager.getWorkspaceData(
+ envConfig.SMELL_MAP_KEY!
+ )}`
+ );
+
+ // ===============================================================
+ // REGISTER COMMANDS
+ // ===============================================================
+
+ // Register Detect Smells Command
+ let detectSmellsCmd = vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.detectSmells',
+ async () => {
+ console.log('Command detectSmells triggered');
+ detectSmells(contextManager);
+ }
+ );
+ context.subscriptions.push(detectSmellsCmd);
+
+ // Register Refactor Smell Command
+ let refactorSmellCmd = vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.refactorSmell',
+ () => {
+ console.log('Command refactorSmells triggered');
+ vscode.window.showInformationMessage('Eco: Detecting smells...');
+ refactorSelectedSmell(contextManager);
+ }
+ );
+ context.subscriptions.push(refactorSmellCmd);
+
+ // ===============================================================
+ // ADD LISTENERS
+ // ===============================================================
+
+ // Adds comments to lines describing the smell
+ context.subscriptions.push(
+ vscode.window.onDidChangeTextEditorSelection((event) => {
+ console.log(`Detected event: ${event.kind?.toString()}`);
+
+ const lineSelectManager = new LineSelectionManager(contextManager);
+ lineSelectManager.commentLine(event.textEditor);
+ })
+ );
+
+ // Updates directory of file states (for checking if modified)
+ context.subscriptions.push(
+ vscode.workspace.onDidSaveTextDocument(async (document) => {
+ const lastSavedHashes = contextManager.getWorkspaceData(
+ envConfig.FILE_CHANGES_KEY!,
+ {}
+ );
+ const lastHash = lastSavedHashes[document.fileName];
+ const currentHash = hashContent(document.getText());
+
+ if (lastHash !== undefined && lastHash !== currentHash) {
+ console.log(
+ `Document ${document.uri.fsPath} has changed since last save.`
+ );
+ }
+
+ // Update the hash in workspace storage
+ await updateLastSavedHash(contextManager, document);
+ })
+ );
+
+ // Initializes first state of document when opened while extension active
+ context.subscriptions.push(
+ vscode.workspace.onDidOpenTextDocument(async (document) => {
+ console.log('Detected document opening');
+ const HASH_STORAGE_KEY = envConfig.FILE_CHANGES_KEY!;
+ const lastSavedHashes = contextManager.getWorkspaceData(
+ HASH_STORAGE_KEY,
+ {}
+ );
+ const lastHash = lastSavedHashes[document.fileName];
+ if (!lastHash) {
+ console.log(
+ `Saving current state of ${document.uri.fsPath.split('/').at(-1)}.`
+ );
+ await updateLastSavedHash(contextManager, document);
+ }
+ })
+ );
}
export function deactivate() {
- console.log("Refactor Plugin deactivated");
+ console.log('Refactor Plugin deactivated');
+}
+
+// ===============================================================
+// UTILITY FUNCTIONS
+// ===============================================================
+
+// Function to hash the document content
+export function hashContent(content: string): string {
+ return crypto.createHash('sha256').update(content).digest('hex');
+}
+
+// Function to update the stored hashes in workspace storage
+async function updateLastSavedHash(
+ contextManager: ContextManager,
+ document: vscode.TextDocument
+) {
+ const lastSavedHashes = contextManager.getWorkspaceData(
+ envConfig.FILE_CHANGES_KEY!,
+ {}
+ );
+ const currentHash = hashContent(document.getText());
+ lastSavedHashes[document.fileName] = currentHash;
+ await contextManager.setWorkspaceData(
+ envConfig.FILE_CHANGES_KEY!,
+ lastSavedHashes
+ );
}
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index 6956708..52a36c9 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -3,8 +3,28 @@ import { getEditor } from '../utils/editorUtils';
export class FileHighlighter {
static highlightSmells(editor: vscode.TextEditor, smells: Smell[]) {
- const yellowUnderline = vscode.window.createTextEditorDecorationType({
- textDecoration: 'underline yellow'
+ const underline = vscode.window.createTextEditorDecorationType({
+ textDecoration: 'wavy rgba(76, 245, 96, 0.62) underline 1px'
+ });
+
+ const flashlight = vscode.window.createTextEditorDecorationType({
+ isWholeLine: true,
+ backgroundColor: 'rgba(249, 209, 10, 0.3)'
+ });
+
+ const aLittleExtra = vscode.window.createTextEditorDecorationType({
+ // isWholeLine: true,
+ borderWidth: '1px 2px 1px 0', // Top, Right, Bottom, No Left border
+ borderStyle: 'solid',
+ borderColor: 'rgba(76, 245, 96, 0.62)', // Change as needed
+ after: {
+ contentText: 'βΆ', // Unicode right arrow
+ margin: '0 0 0 5px', // Space between line and arrow
+ color: 'rgba(76, 245, 96, 0.62)',
+ fontWeight: 'bold'
+ },
+ overviewRulerColor: 'rgba(76, 245, 96, 0.62)',
+ overviewRulerLane: vscode.OverviewRulerLane.Right
});
const decorations: vscode.DecorationOptions[] = smells
@@ -20,7 +40,7 @@ export class FileHighlighter {
const line_text = editor.document.lineAt(line).text;
const line_length = line_text.length;
const indexStart = line_length - line_text.trimStart().length;
- const indexEnd = line_text.trimEnd().length;
+ const indexEnd = line_text.trimEnd().length + 1;
const range = new vscode.Range(line, indexStart, line, indexEnd);
@@ -28,7 +48,7 @@ export class FileHighlighter {
});
});
- editor.setDecorations(yellowUnderline, decorations);
+ editor.setDecorations(flashlight, decorations);
console.log('Updated smell line highlights');
}
}
diff --git a/src/ui/lineSelectionManager.ts b/src/ui/lineSelectionManager.ts
new file mode 100644
index 0000000..ccb94f1
--- /dev/null
+++ b/src/ui/lineSelectionManager.ts
@@ -0,0 +1,95 @@
+import * as vscode from 'vscode';
+import { ContextManager } from '../context/contextManager';
+import { envConfig } from '../utils/envConfig';
+import { SmellDetectRecord } from '../commands/detectSmells';
+import { hashContent } from '../extension';
+
+export class LineSelectionManager {
+ private contextManager;
+ private decoration: vscode.TextEditorDecorationType | null = null;
+
+ public constructor(contextManager: ContextManager) {
+ this.contextManager = contextManager;
+ }
+
+ public removeLastComment() {
+ if (this.decoration) {
+ console.log('Removing decoration');
+ this.decoration.dispose();
+ }
+ }
+
+ public commentLine(editor: vscode.TextEditor) {
+ this.removeLastComment();
+
+ const filePath = editor.document.fileName;
+ const smellsDetectRecord = this.contextManager.getWorkspaceData(
+ envConfig.SMELL_MAP_KEY!
+ )[filePath] as SmellDetectRecord;
+
+ if (!smellsDetectRecord) {
+ return;
+ }
+
+ if (smellsDetectRecord.hash !== hashContent(editor.document.getText())) {
+ return;
+ }
+
+ const { selection } = editor;
+
+ if (!selection.isSingleLine) {
+ return;
+ }
+
+ const selectedLine = selection.start.line;
+ console.log(`selection: ${selectedLine}`);
+
+ const smells = smellsDetectRecord.smells;
+
+ const smellsAtLine = smells.filter((smell) => {
+ const numOcc = smell.occurences.filter((occ) => {
+ return occ.line === selectedLine + 1;
+ });
+ return numOcc.length > 0;
+ });
+
+ if (smellsAtLine.length === 0) {
+ return;
+ }
+
+ let comment;
+
+ if (smellsAtLine.length > 1) {
+ comment = `π Smell: ${smellsAtLine[0].symbol} | ...`;
+ } else {
+ comment = `π Smell: ${smellsAtLine[0].symbol}`;
+ }
+
+ this.decoration = vscode.window.createTextEditorDecorationType({
+ isWholeLine: true,
+ after: {
+ contentText: comment,
+ color: 'rgb(153, 211, 212)', // Red-orange for visibility
+ margin: '0 0 0 10px', // Moves it to the right edge
+ textDecoration: 'none'
+ }
+ });
+
+ if (!editor) {
+ return;
+ }
+
+ const selectionLine: vscode.Range[] = [];
+
+ const line_text = editor.document.lineAt(selectedLine).text;
+ const line_length = line_text.length;
+ const indexStart = line_length - line_text.trimStart().length;
+ const indexEnd = line_text.trimEnd().length + 1;
+
+ selectionLine.push(
+ new vscode.Range(selectedLine, indexStart, selectedLine, indexEnd)
+ );
+
+ editor.setDecorations(this.decoration, selectionLine);
+ }
+}
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts
new file mode 100644
index 0000000..a4b36ce
--- /dev/null
+++ b/src/utils/envConfig.ts
@@ -0,0 +1,13 @@
+import * as dotenv from 'dotenv';
+
+dotenv.config();
+
+export interface EnvConfig {
+ SMELL_MAP_KEY?: string;
+ FILE_CHANGES_KEY?: string;
+}
+
+export const envConfig: EnvConfig = {
+ SMELL_MAP_KEY: process.env.SMELL_MAP_KEY,
+ FILE_CHANGES_KEY: process.env.FILE_CHANGES_KEY
+};
diff --git a/webpack.config.js b/webpack.config.js
index 37d7024..56b3e0a 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,6 +3,7 @@
'use strict';
const path = require('path');
+const Dotenv = require('dotenv-webpack');
//@ts-check
/** @typedef {import('webpack').Configuration} WebpackConfig **/
@@ -10,7 +11,7 @@ const path = require('path');
/** @type WebpackConfig */
const extensionConfig = {
target: 'node', // VS Code extensions run in a Node.js-context π -> https://webpack.js.org/configuration/node/
- mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
+ mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
entry: './src/extension.ts', // the entry point of this extension, π -> https://webpack.js.org/configuration/entry-context/
output: {
@@ -42,7 +43,12 @@ const extensionConfig = {
},
devtool: 'nosources-source-map',
infrastructureLogging: {
- level: "log", // enables logging required for problem matchers
+ level: 'log' // enables logging required for problem matchers
},
+ plugins: [
+ new Dotenv({
+ path: './.env' // Path to your .env file
+ })
+ ]
};
-module.exports = [ extensionConfig ];
\ No newline at end of file
+module.exports = [extensionConfig];
From e71d6bfa3fa4f6884e9b11d5a8debd280a1ec9f8 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Wed, 29 Jan 2025 18:09:35 -0500
Subject: [PATCH 05/83] Made updates to README.md
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 9f7fad6..52f8ab2 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,11 @@ VS Code Plugin for Source Code Optimizer
1. clone this repo
2. open terminal and write:
- npm install
- npm run compile
+ `npm install` (only the first time)
+ `npm run compile` | `npm run watch` <- second command will auto compile on save
3. open another vs code window with the ecooptimzer repo
4. start venv in the ecooptimizer repo
5. run "python -m ecoopitmizer.api.main" in terminal to start the developement server manually
-6. come back to this repo, go to run and debug
+6. come back to this repo, go to run and debug (or just click `F5` key)
7. run extension (should open a new vs code window so open the repo u want in this)
-8. in the vscode search bar type ">eco: detect smells" and run it
+8. in the vscode search bar (`ctrl+shift+p`) type ">eco: detect smells" and run it
From 019d57ebd995c461203285197a2306485abc3524 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan <167944429+nivethakuruparan@users.noreply.github.com>
Date: Wed, 29 Jan 2025 22:29:10 -0500
Subject: [PATCH 06/83] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 52f8ab2..fe74f71 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ VS Code Plugin for Source Code Optimizer
`npm run compile` | `npm run watch` <- second command will auto compile on save
3. open another vs code window with the ecooptimzer repo
4. start venv in the ecooptimizer repo
-5. run "python -m ecoopitmizer.api.main" in terminal to start the developement server manually
+5. run "python -m ecooptimizer.api.main" in terminal to start the developement server manually
6. come back to this repo, go to run and debug (or just click `F5` key)
7. run extension (should open a new vs code window so open the repo u want in this)
8. in the vscode search bar (`ctrl+shift+p`) type ">eco: detect smells" and run it
From 658bf9603be3aef923aac4fe8e9233a028445414 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 30 Jan 2025 01:48:14 -0500
Subject: [PATCH 07/83] bug fixes
---
src/api/backend.ts | 12 +++++--
src/commands/detectSmells.ts | 3 +-
src/commands/refactorSmell.ts | 38 ++++++++++++++--------
src/extension.ts | 60 ++++++++++++++---------------------
src/global.d.ts | 11 +++++--
src/ui/fileHighlighter.ts | 36 ++++++++++++++++++---
src/utils/editorUtils.ts | 35 ++++++++++++++++----
7 files changed, 127 insertions(+), 68 deletions(-)
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 64f54f9..db8a8ed 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,3 +1,6 @@
+import * as fs from 'fs';
+import path from 'path';
+
const BASE_URL = 'http://127.0.0.1:8000'; // API URL for Python backend
// Fetch detected smells for a given file
@@ -23,10 +26,13 @@ export async function refactorSmell(
): Promise {
const url = `${BASE_URL}/refactor`;
const payload = {
- file_path: filePath,
- smell: smell
+ source_dir: path.dirname(filePath),
+ smell
};
+ console.log(`workspace: ${payload.source_dir}`);
+ console.log(`${smell.path}`);
+
try {
const response = await fetch(url, {
method: 'POST',
@@ -37,7 +43,7 @@ export async function refactorSmell(
});
if (!response.ok) {
- throw new Error(`Error refactoring smell: ${response.statusText}`);
+ throw new Error(`Error refactoring smell: ${await response.text()}`);
}
const refactorResult = (await response.json()) as RefactorOutput;
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 5070176..4d73925 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -108,7 +108,8 @@ export async function detectSmells(contextManager: ContextManager) {
`Eco: Detected ${smellsData.length} smells in the file.`
);
- FileHighlighter.highlightSmells(editor, smellsData);
+ const fileHighlighter = new FileHighlighter(contextManager);
+ fileHighlighter.highlightSmells(editor, smellsData);
vscode.window.showInformationMessage(
'Eco: Detected code smells have been highlighted.'
);
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 1f95c58..c7ef222 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -6,6 +6,7 @@ import { Smell } from '../types';
import { fetchSmells, refactorSmell } from '../api/backend';
import * as fs from 'fs';
import { ContextManager } from '../context/contextManager';
+import { envConfig } from '../utils/envConfig';
async function refactorLine(
smell: Smell,
@@ -43,7 +44,9 @@ export async function refactorSelectedSmell(contextManager: ContextManager) {
// only account for one selection to be refactored for now
const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
- const smellsData = await fetchSmells(filePath);
+ const smellsData: Smell[] = contextManager.getWorkspaceData(
+ envConfig.SMELL_MAP_KEY!
+ )[filePath].smells;
if (!smellsData || smellsData.length === 0) {
vscode.window.showErrorMessage(
'Eco: No smells detected in the file for refactoring.'
@@ -83,22 +86,31 @@ export async function refactorSelectedSmell(contextManager: ContextManager) {
}
const { refactoredData, updatedSmells } = refactorResult;
+ if (!refactoredData) {
+ vscode.window.showErrorMessage(
+ 'Eco: Refactoring failed. See console for details.'
+ );
+ return;
+ }
+
// Did not test this yet, but if it works need to change so that all modified files are displayed
// only shows the file where the smell was found
- fs.readFile(refactoredData.targetFile, async (err, data) => {
- if (err) {
- throw err;
- }
- await RefactorManager.previewRefactor(editor, data.toString('utf8'));
- vscode.window.showInformationMessage(
- `Eco: Refactoring completed. Energy difference: ${refactoredData.energySaved.toFixed(
- 4
- )}`
- );
- });
+ console.log(`target file: ${refactoredData.targetFile}`);
+ // fs.readFile(refactoredData.targetFile, async (err, data) => {
+ // if (err) {
+ // throw err;
+ // }
+ // await RefactorManager.previewRefactor(editor, data.toString('utf8'));
+ // vscode.window.showInformationMessage(
+ // `Eco: Refactoring completed. Energy difference: ${refactoredData.energySaved.toFixed(
+ // 4
+ // )}`
+ // );
+ // });
if (updatedSmells.length) {
- FileHighlighter.highlightSmells(editor, updatedSmells);
+ const fileHighlighter = new FileHighlighter(contextManager);
+ fileHighlighter.highlightSmells(editor, smellsData);
} else {
vscode.window.showWarningMessage(
'Eco: No updated smells detected after refactoring.'
diff --git a/src/extension.ts b/src/extension.ts
index 872c508..6fb0ca7 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -75,11 +75,11 @@ export function activate(context: vscode.ExtensionContext) {
// ===============================================================
// Adds comments to lines describing the smell
+ const lineSelectManager = new LineSelectionManager(contextManager);
context.subscriptions.push(
vscode.window.onDidChangeTextEditorSelection((event) => {
console.log(`Detected event: ${event.kind?.toString()}`);
- const lineSelectManager = new LineSelectionManager(contextManager);
lineSelectManager.commentLine(event.textEditor);
})
);
@@ -87,41 +87,22 @@ export function activate(context: vscode.ExtensionContext) {
// Updates directory of file states (for checking if modified)
context.subscriptions.push(
vscode.workspace.onDidSaveTextDocument(async (document) => {
- const lastSavedHashes = contextManager.getWorkspaceData(
- envConfig.FILE_CHANGES_KEY!,
- {}
- );
- const lastHash = lastSavedHashes[document.fileName];
- const currentHash = hashContent(document.getText());
-
- if (lastHash !== undefined && lastHash !== currentHash) {
- console.log(
- `Document ${document.uri.fsPath} has changed since last save.`
- );
- }
-
- // Update the hash in workspace storage
- await updateLastSavedHash(contextManager, document);
+ await updateHash(contextManager, document);
})
);
+ // Handles case of documents already being open on vscode open
+ vscode.window.visibleTextEditors.forEach(async (editor) => {
+ if (editor.document) {
+ await updateHash(contextManager, editor.document);
+ }
+ });
+
// Initializes first state of document when opened while extension active
context.subscriptions.push(
- vscode.workspace.onDidOpenTextDocument(async (document) => {
- console.log('Detected document opening');
- const HASH_STORAGE_KEY = envConfig.FILE_CHANGES_KEY!;
- const lastSavedHashes = contextManager.getWorkspaceData(
- HASH_STORAGE_KEY,
- {}
- );
- const lastHash = lastSavedHashes[document.fileName];
- if (!lastHash) {
- console.log(
- `Saving current state of ${document.uri.fsPath.split('/').at(-1)}.`
- );
- await updateLastSavedHash(contextManager, document);
- }
- })
+ vscode.workspace.onDidOpenTextDocument(
+ async (document) => await updateHash(contextManager, document)
+ )
);
}
@@ -139,7 +120,7 @@ export function hashContent(content: string): string {
}
// Function to update the stored hashes in workspace storage
-async function updateLastSavedHash(
+async function updateHash(
contextManager: ContextManager,
document: vscode.TextDocument
) {
@@ -147,10 +128,15 @@ async function updateLastSavedHash(
envConfig.FILE_CHANGES_KEY!,
{}
);
+ const lastHash = lastSavedHashes[document.fileName];
const currentHash = hashContent(document.getText());
- lastSavedHashes[document.fileName] = currentHash;
- await contextManager.setWorkspaceData(
- envConfig.FILE_CHANGES_KEY!,
- lastSavedHashes
- );
+
+ if (lastHash !== undefined && lastHash !== currentHash) {
+ console.log(`Document ${document.uri.fsPath} has changed since last save.`);
+ lastSavedHashes[document.fileName] = currentHash;
+ await contextManager.setWorkspaceData(
+ envConfig.FILE_CHANGES_KEY!,
+ lastSavedHashes
+ );
+ }
}
diff --git a/src/global.d.ts b/src/global.d.ts
index 44abb2a..749d017 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -10,6 +10,13 @@ declare global {
endLine?: number;
column: number;
endColumn?: number;
+ callString?: string;
+ }
+
+ interface AdditionalInfo {
+ repetitions?: number;
+ concatTarget?: string;
+ innerLoopLine?: number;
}
interface Smell {
@@ -22,11 +29,11 @@ declare global {
module: string; // Optional: Module name
obj: string; // Optional: Object name associated with the smell (if applicable)
occurences: Occurrence[]; // Optional: List of occurrences for repeated calls
- additionalInfo?: any;
+ additionalInfo?: AdditionalInfo;
}
interface RefactoredData {
- temp_dir: string;
+ tempDir: string;
targetFile: string;
energySaved: number;
refactoredFiles: string[];
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index 52a36c9..1720025 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -1,8 +1,25 @@
import * as vscode from 'vscode';
import { getEditor } from '../utils/editorUtils';
+import { ContextManager } from '../context/contextManager';
export class FileHighlighter {
- static highlightSmells(editor: vscode.TextEditor, smells: Smell[]) {
+ private contextManager;
+ private decoration: vscode.TextEditorDecorationType | null = null;
+
+ public constructor(contextManager: ContextManager) {
+ this.contextManager = contextManager;
+ }
+
+ public resetHighlights() {
+ if (this.decoration) {
+ console.log('Removing decoration');
+ this.decoration.dispose();
+ }
+ }
+
+ public highlightSmells(editor: vscode.TextEditor, smells: Smell[]) {
+ this.resetHighlights();
+
const underline = vscode.window.createTextEditorDecorationType({
textDecoration: 'wavy rgba(76, 245, 96, 0.62) underline 1px'
});
@@ -13,7 +30,6 @@ export class FileHighlighter {
});
const aLittleExtra = vscode.window.createTextEditorDecorationType({
- // isWholeLine: true,
borderWidth: '1px 2px 1px 0', // Top, Right, Bottom, No Left border
borderStyle: 'solid',
borderColor: 'rgba(76, 245, 96, 0.62)', // Change as needed
@@ -27,7 +43,13 @@ export class FileHighlighter {
overviewRulerLane: vscode.OverviewRulerLane.Right
});
- const decorations: vscode.DecorationOptions[] = smells
+ const padding = vscode.window.createTextEditorDecorationType({
+ after: {
+ contentText: ' '
+ }
+ });
+
+ const smellLines: vscode.DecorationOptions[] = smells
.filter((smell: Smell) =>
smell.occurences.every((occurrence: { line: number }) =>
isValidLine(occurrence.line)
@@ -40,7 +62,7 @@ export class FileHighlighter {
const line_text = editor.document.lineAt(line).text;
const line_length = line_text.length;
const indexStart = line_length - line_text.trimStart().length;
- const indexEnd = line_text.trimEnd().length + 1;
+ const indexEnd = line_text.trimEnd().length + 2;
const range = new vscode.Range(line, indexStart, line, indexEnd);
@@ -48,7 +70,11 @@ export class FileHighlighter {
});
});
- editor.setDecorations(flashlight, decorations);
+ this.decoration = aLittleExtra;
+
+ editor.setDecorations(padding, smellLines);
+ editor.setDecorations(this.decoration, smellLines);
+
console.log('Updated smell line highlights');
}
}
diff --git a/src/utils/editorUtils.ts b/src/utils/editorUtils.ts
index ab6c92f..162b760 100644
--- a/src/utils/editorUtils.ts
+++ b/src/utils/editorUtils.ts
@@ -2,19 +2,40 @@ import * as vscode from 'vscode';
/**
* Gets the active editor and its file path if an editor is open.
- * @returns {{ editor: vscode.TextEditor | undefined, filePath: string | undefined }}
+ * @returns {{ editor: vscode.TextEditor | undefined, filePath: string | undefined }}
* An object containing the active editor and the file path, or undefined for both if no editor is open.
*/
-export function getEditorAndFilePath(): {editor: vscode.TextEditor | undefined, filePath: string | undefined}{
- const activeEditor = vscode.window.activeTextEditor;
- const filePath = activeEditor?.document.uri.fsPath;
- return { editor: activeEditor, filePath };
+export function getEditorAndFilePath(): {
+ editor: vscode.TextEditor | undefined;
+ filePath: string | undefined;
+} {
+ const activeEditor = vscode.window.activeTextEditor;
+ const filePath = activeEditor?.document.uri.fsPath;
+ return { editor: activeEditor, filePath };
}
+// /**
+// * Gets the active editor, its file path and the workspace that contains it if an editor is open.
+// * @returns {{ editor: vscode.TextEditor | undefined, filePath: string | undefined, workspace: string | undefined }}
+// * An object containing the active editor, the file path and workspace, or undefined for all three if no editor is open.
+// */
+// export function getEditorAndPaths(): {
+// editor: vscode.TextEditor | undefined;
+// filePath: string | undefined;
+// workspace: string | undefined;
+// } {
+// const activeEditor = vscode.window.activeTextEditor;
+// const fileUri = activeEditor?.document.uri!;
+// const workspace = vscode.workspace.getWorkspaceFolder(fileUri)?.uri.fsPath;
+// return {
+// editor: activeEditor,
+// filePath: fileUri.fsPath,
+// workspace: workspace
+// };
+// }
/**
* Gets the active editor if an editor is open.
*/
export function getEditor(): vscode.TextEditor | undefined {
- return vscode.window.activeTextEditor;
+ return vscode.window.activeTextEditor;
}
-
From 05720400f777bd8f5e168ac6a7049718c1bf0ab1 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 30 Jan 2025 02:06:06 -0500
Subject: [PATCH 08/83] fixed bugs in type definitions
---
src/commands/refactorSmell.ts | 22 +++++++++++-----------
src/global.d.ts | 2 +-
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index c7ef222..c99f2e6 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -96,17 +96,17 @@ export async function refactorSelectedSmell(contextManager: ContextManager) {
// Did not test this yet, but if it works need to change so that all modified files are displayed
// only shows the file where the smell was found
console.log(`target file: ${refactoredData.targetFile}`);
- // fs.readFile(refactoredData.targetFile, async (err, data) => {
- // if (err) {
- // throw err;
- // }
- // await RefactorManager.previewRefactor(editor, data.toString('utf8'));
- // vscode.window.showInformationMessage(
- // `Eco: Refactoring completed. Energy difference: ${refactoredData.energySaved.toFixed(
- // 4
- // )}`
- // );
- // });
+ fs.readFile(refactoredData.targetFile, async (err, data) => {
+ if (err) {
+ throw err;
+ }
+ await RefactorManager.previewRefactor(editor, data.toString('utf8'));
+ vscode.window.showInformationMessage(
+ `Eco: Refactoring completed. Energy difference: ${refactoredData.energySaved.toFixed(
+ 4
+ )}`
+ );
+ });
if (updatedSmells.length) {
const fileHighlighter = new FileHighlighter(contextManager);
diff --git a/src/global.d.ts b/src/global.d.ts
index 749d017..f298643 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -40,7 +40,7 @@ declare global {
}
interface RefactorOutput {
- refactoredData: RefactoredData; // Refactored code as a string
+ refactoredData?: RefactoredData; // Refactored code as a string
updatedSmells: Smell[]; //
}
}
From 7d6f5e7aa929ae97ba00f1cac2195263d4afd4fe Mon Sep 17 00:00:00 2001
From: mya
Date: Thu, 30 Jan 2025 02:28:56 -0500
Subject: [PATCH 09/83] Stage 1 adding difference UI
---
src/commands/refactorSmell.ts | 5 +-
src/ui/diffViewer.ts | 104 ++++++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+), 1 deletion(-)
create mode 100644 src/ui/diffViewer.ts
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 1f95c58..ff0704c 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -6,6 +6,7 @@ import { Smell } from '../types';
import { fetchSmells, refactorSmell } from '../api/backend';
import * as fs from 'fs';
import { ContextManager } from '../context/contextManager';
+import { showDiffViewer } from '../ui/diffViewer';
async function refactorLine(
smell: Smell,
@@ -89,7 +90,9 @@ export async function refactorSelectedSmell(contextManager: ContextManager) {
if (err) {
throw err;
}
- await RefactorManager.previewRefactor(editor, data.toString('utf8'));
+
+ // await RefactorManager.previewRefactor(editor, data.toString('utf8')); mya commented to test my difference library stuff
+ await showDiffViewer(editor, data.toString('utf8'), "bumb");
vscode.window.showInformationMessage(
`Eco: Refactoring completed. Energy difference: ${refactoredData.energySaved.toFixed(
4
diff --git a/src/ui/diffViewer.ts b/src/ui/diffViewer.ts
new file mode 100644
index 0000000..bacc8db
--- /dev/null
+++ b/src/ui/diffViewer.ts
@@ -0,0 +1,104 @@
+import * as vscode from 'vscode';
+import { diffWords } from 'diff';
+
+/**
+ * Displays a WebView panel to show a visual diff between the original and refactored code.
+ * Users can accept or reject the changes.
+ */
+export async function showDiffViewer(editor: vscode.TextEditor, refactoredCode: string, originalCode: string) {
+ const panel = vscode.window.createWebviewPanel(
+ 'ecoDiffViewer',
+ 'Eco: Code Refactor Preview',
+ vscode.ViewColumn.Two,
+ { enableScripts: true }
+ );
+
+ const diffHtml = generateDiffHtml(originalCode, refactoredCode);
+
+ panel.webview.html = `
+
+
+
+
+
+ Eco: Code Refactor Preview
+
+
+
+ Refactoring Preview
+
+
${diffHtml.original}
+
${diffHtml.refactored}
+
+
+
+
+
+
+
+ `;
+
+ // Handle messages from WebView
+ panel.webview.onDidReceiveMessage(
+ (message) => {
+ if (message.command === 'accept') {
+ applyRefactoredCode(editor, refactoredCode);
+ panel.dispose();
+ } else if (message.command === 'reject') {
+ panel.dispose();
+ }
+ },
+ []
+ );
+}
+
+/**
+ * Generates side-by-side HTML diff highlighting differences.
+ */
+function generateDiffHtml(original: string, refactored: string) {
+ const diff = diffWords(original, refactored);
+
+ let originalHtml = '';
+ let refactoredHtml = '';
+
+ diff.forEach((part) => {
+ if (part.added) {
+ refactoredHtml += `${part.value}`;
+ } else if (part.removed) {
+ originalHtml += `${part.value}`;
+ } else {
+ originalHtml += part.value;
+ refactoredHtml += part.value;
+ }
+ });
+
+ return { original: originalHtml, refactored: refactoredHtml };
+}
+
+/**
+ * Replaces the selected code in the editor with the refactored version.
+ */
+function applyRefactoredCode(editor: vscode.TextEditor, newCode: string) {
+ editor.edit((editBuilder) => {
+ const fullRange = new vscode.Range(
+ new vscode.Position(0, 0),
+ new vscode.Position(editor.document.lineCount, 0)
+ );
+ editBuilder.replace(fullRange, newCode);
+ });
+}
From 3afbb70667d6714c061b8cc822d47985f76209f0 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 30 Jan 2025 13:41:30 -0500
Subject: [PATCH 10/83] Fixed smell data mismatch that caused refactorings to
fail
---
src/api/backend.ts | 5 ++++-
src/commands/refactorSmell.ts | 4 +++-
src/global.d.ts | 8 +++++---
src/types.ts | 27 ++++++++++++++++++++++++---
src/ui/fileHighlighter.ts | 18 ++++++++----------
src/ui/lineSelectionManager.ts | 5 +----
6 files changed, 45 insertions(+), 22 deletions(-)
diff --git a/src/api/backend.ts b/src/api/backend.ts
index db8a8ed..fe20489 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,5 +1,6 @@
import * as fs from 'fs';
import path from 'path';
+import { Smell, RefactorOutput } from '../types';
const BASE_URL = 'http://127.0.0.1:8000'; // API URL for Python backend
@@ -12,6 +13,7 @@ export async function fetchSmells(filePath: string): Promise {
throw new Error(`Error fetching smells: ${response.statusText}`);
}
const smellsList = (await response.json()) as Smell[];
+ smellsList.forEach((smell) => console.log(JSON.stringify(smell)));
return smellsList;
} catch (error) {
console.error('Error in getSmells:', error);
@@ -25,12 +27,13 @@ export async function refactorSmell(
smell: Smell
): Promise {
const url = `${BASE_URL}/refactor`;
+
const payload = {
source_dir: path.dirname(filePath),
smell
};
- console.log(`workspace: ${payload.source_dir}`);
+ console.log(`payload: ${JSON.stringify(payload)}`);
console.log(`${smell.path}`);
try {
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index c99f2e6..9f9d681 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -2,8 +2,8 @@ import * as vscode from 'vscode';
import { RefactorManager } from '../ui/refactorManager';
import { getEditorAndFilePath } from '../utils/editorUtils';
import { FileHighlighter } from '../ui/fileHighlighter';
+import { refactorSmell } from '../api/backend';
import { Smell } from '../types';
-import { fetchSmells, refactorSmell } from '../api/backend';
import * as fs from 'fs';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
@@ -58,6 +58,8 @@ export async function refactorSelectedSmell(contextManager: ContextManager) {
const matchingSmells = smellsData.filter((smell: Smell) => {
return selectedLine === smell.occurences[0].line;
});
+ console.log(JSON.stringify(matchingSmells));
+ console.log('===========================================================');
if (matchingSmells.length === 0) {
vscode.window.showInformationMessage(
diff --git a/src/global.d.ts b/src/global.d.ts
index f298643..2d13903 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -10,11 +10,13 @@ declare global {
endLine?: number;
column: number;
endColumn?: number;
- callString?: string;
}
interface AdditionalInfo {
+ // CRC
repetitions?: number;
+ callString?: string;
+ // SCL
concatTarget?: string;
innerLoopLine?: number;
}
@@ -27,9 +29,9 @@ declare global {
confidence: string; // Confidence level (e.g., "HIGH", "MEDIUM")
path: string; // Optional: absolute file path
module: string; // Optional: Module name
- obj: string; // Optional: Object name associated with the smell (if applicable)
+ obj?: string; // Optional: Object name associated with the smell (if applicable)
occurences: Occurrence[]; // Optional: List of occurrences for repeated calls
- additionalInfo?: AdditionalInfo;
+ additionalInfo: AdditionalInfo;
}
interface RefactoredData {
diff --git a/src/types.ts b/src/types.ts
index 65cb228..09e25d6 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,10 +1,19 @@
-interface Occurrence {
+export interface Occurrence {
line: number;
endLine?: number;
column: number;
endColumn?: number;
}
+export interface AdditionalInfo {
+ // CRC
+ repetitions?: number;
+ callString?: string;
+ // SCL
+ concatTarget?: string;
+ innerLoopLine?: number;
+}
+
export interface Smell {
type: string; // Type of the smell (e.g., "performance", "convention")
symbol: string; // Symbolic identifier for the smell (e.g., "cached-repeated-calls")
@@ -13,7 +22,19 @@ export interface Smell {
confidence: string; // Confidence level (e.g., "HIGH", "MEDIUM")
path: string; // Optional: absolute file path
module: string; // Optional: Module name
- obj: string; // Optional: Object name associated with the smell (if applicable)
+ obj?: string; // Optional: Object name associated with the smell (if applicable)
occurences: Occurrence[]; // Optional: List of occurrences for repeated calls
- additionalInfo?: any;
+ additionalInfo: AdditionalInfo;
+}
+
+export interface RefactoredData {
+ tempDir: string;
+ targetFile: string;
+ energySaved: number;
+ refactoredFiles: string[];
+}
+
+export interface RefactorOutput {
+ refactoredData?: RefactoredData; // Refactored code as a string
+ updatedSmells: Smell[]; //
}
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index 1720025..1a561df 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -55,19 +55,17 @@ export class FileHighlighter {
isValidLine(occurrence.line)
)
)
- .flatMap((smell: any) => {
- return smell.occurences.map((occurrence: { line: number }) => {
- const line = occurrence.line - 1; // convert to zero-based line index for VS editor
+ .map((smell: Smell) => {
+ const line = smell.occurences[0].line - 1; // convert to zero-based line index for VS editor
- const line_text = editor.document.lineAt(line).text;
- const line_length = line_text.length;
- const indexStart = line_length - line_text.trimStart().length;
- const indexEnd = line_text.trimEnd().length + 2;
+ const line_text = editor.document.lineAt(line).text;
+ const line_length = line_text.length;
+ const indexStart = line_length - line_text.trimStart().length;
+ const indexEnd = line_text.trimEnd().length + 2;
- const range = new vscode.Range(line, indexStart, line, indexEnd);
+ const range = new vscode.Range(line, indexStart, line, indexEnd);
- return { range, hoverMessage: `Smell: ${smell.message}` }; // option to hover over and read smell details
- });
+ return { range, hoverMessage: `Smell: ${smell.message}` }; // option to hover over and read smell details
});
this.decoration = aLittleExtra;
diff --git a/src/ui/lineSelectionManager.ts b/src/ui/lineSelectionManager.ts
index ccb94f1..85b1087 100644
--- a/src/ui/lineSelectionManager.ts
+++ b/src/ui/lineSelectionManager.ts
@@ -47,10 +47,7 @@ export class LineSelectionManager {
const smells = smellsDetectRecord.smells;
const smellsAtLine = smells.filter((smell) => {
- const numOcc = smell.occurences.filter((occ) => {
- return occ.line === selectedLine + 1;
- });
- return numOcc.length > 0;
+ return smell.occurences[0].line === selectedLine + 1;
});
if (smellsAtLine.length === 0) {
From f95cd7596238cf05a3761ffe58381c87cdf873e8 Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Thu, 30 Jan 2025 14:15:41 -0500
Subject: [PATCH 11/83] started working custom hover message
---
package-lock.json | 11 +++
package.json | 1 +
src/commands/detectSmells.ts | 10 +--
src/commands/refactorSmell.ts | 7 +-
src/extension.ts | 4 +-
src/ui/fileHighlighter.ts | 7 +-
src/ui/hoverManager.ts | 125 ++++++++++++++++++++++++++++++++++
7 files changed, 155 insertions(+), 10 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index ab02400..11d99d8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "ecooptimizer-vs-code-plugin",
"version": "0.0.1",
"dependencies": {
+ "@types/dotenv": "^6.1.1",
"dotenv": "^16.4.7",
"dotenv-webpack": "^8.1.0"
},
@@ -412,6 +413,15 @@
"node": ">=14"
}
},
+ "node_modules/@types/dotenv": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
+ "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/eslint": {
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
@@ -1653,6 +1663,7 @@
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
diff --git a/package.json b/package.json
index 9ea979f..bc42a93 100644
--- a/package.json
+++ b/package.json
@@ -62,6 +62,7 @@
"webpack-cli": "^5.1.4"
},
"dependencies": {
+ "@types/dotenv": "^6.1.1",
"dotenv": "^16.4.7",
"dotenv-webpack": "^8.1.0"
}
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 4d73925..5e43847 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -6,6 +6,8 @@ import { Smell } from '../types';
import { fetchSmells } from '../api/backend';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
+// import { HoverManager } from "../ui/hoverManager"; // Import the HoverManager
+
export interface SmellDetectRecord {
hash: string;
@@ -30,7 +32,7 @@ export async function getSmells(
}
}
-export async function detectSmells(contextManager: ContextManager) {
+export async function detectSmells(contextManager: ContextManager, context: vscode.ExtensionContext) {
const { editor, filePath } = getEditorAndFilePath();
if (!editor) {
@@ -109,8 +111,8 @@ export async function detectSmells(contextManager: ContextManager) {
);
const fileHighlighter = new FileHighlighter(contextManager);
+ // const hoverManager = new HoverManager(context, smellsData);
fileHighlighter.highlightSmells(editor, smellsData);
- vscode.window.showInformationMessage(
- 'Eco: Detected code smells have been highlighted.'
- );
+ vscode.window.showInformationMessage('Eco: Detected code smells have been highlighted.');
}
+
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 9f9d681..99916c0 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -7,6 +7,8 @@ import { Smell } from '../types';
import * as fs from 'fs';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
+// import { HoverManager } from '../ui/hoverManager';
+
async function refactorLine(
smell: Smell,
@@ -23,7 +25,7 @@ async function refactorLine(
}
}
-export async function refactorSelectedSmell(contextManager: ContextManager) {
+export async function refactorSelectedSmell(contextManager: ContextManager, context: vscode.ExtensionContext) {
const { editor, filePath } = getEditorAndFilePath();
if (!editor) {
@@ -112,7 +114,8 @@ export async function refactorSelectedSmell(contextManager: ContextManager) {
if (updatedSmells.length) {
const fileHighlighter = new FileHighlighter(contextManager);
- fileHighlighter.highlightSmells(editor, smellsData);
+ // const hoverManager = new HoverManager(context, smellsData);
+ fileHighlighter.highlightSmells(editor, updatedSmells);
} else {
vscode.window.showWarningMessage(
'Eco: No updated smells detected after refactoring.'
diff --git a/src/extension.ts b/src/extension.ts
index 6fb0ca7..7d8b511 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -54,7 +54,7 @@ export function activate(context: vscode.ExtensionContext) {
'ecooptimizer-vs-code-plugin.detectSmells',
async () => {
console.log('Command detectSmells triggered');
- detectSmells(contextManager);
+ detectSmells(contextManager, context);
}
);
context.subscriptions.push(detectSmellsCmd);
@@ -65,7 +65,7 @@ export function activate(context: vscode.ExtensionContext) {
() => {
console.log('Command refactorSmells triggered');
vscode.window.showInformationMessage('Eco: Detecting smells...');
- refactorSelectedSmell(contextManager);
+ refactorSelectedSmell(contextManager, context);
}
);
context.subscriptions.push(refactorSmellCmd);
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index 1a561df..212084a 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { getEditor } from '../utils/editorUtils';
import { ContextManager } from '../context/contextManager';
+import { HoverManager } from './hoverManager';
export class FileHighlighter {
private contextManager;
@@ -65,8 +66,10 @@ export class FileHighlighter {
const range = new vscode.Range(line, indexStart, line, indexEnd);
- return { range, hoverMessage: `Smell: ${smell.message}` }; // option to hover over and read smell details
- });
+ const hoverManager = HoverManager.getInstance(this.contextManager, smells);
+ const hoverContent = hoverManager.getHoverContent(editor.document, new vscode.Position(line, indexStart));
+ return { range, hoverMessage: hoverContent || undefined }; // option to hover over and read smell details
+ });
this.decoration = aLittleExtra;
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index e69de29..0fd6c19 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -0,0 +1,125 @@
+import * as vscode from "vscode";
+import { Smell } from "../types";
+import { refactorSelectedSmell } from "../commands/refactorSmell";
+import { ContextManager } from "../context/contextManager";
+
+export class HoverManager {
+ private static instance: HoverManager;
+ private smells: Smell[];
+ private vscodeContext: vscode.ExtensionContext;
+
+ static getInstance(contextManager: ContextManager, smells: Smell[]): HoverManager {
+ if (!HoverManager.instance) {
+ HoverManager.instance = new HoverManager(contextManager, smells);
+ } else {
+ HoverManager.instance.updateSmells(smells);
+ }
+ return HoverManager.instance;
+ }
+
+ private constructor(private contextManager: ContextManager, smells: Smell[]) {
+ this.smells = smells || [];
+ this.vscodeContext = contextManager.context;
+ this.registerHoverProvider();
+ this.registerCommands();
+ }
+
+ private updateSmells(smells: Smell[]): void {
+ this.smells = smells || [];
+ }
+
+ // Register hover provider for Python files
+ private registerHoverProvider(): void {
+ this.vscodeContext.subscriptions.push(
+ vscode.languages.registerHoverProvider(
+ { scheme: "file", language: "python" },
+ {
+ provideHover: (document, position, token) => {
+ const hoverContent = this.getHoverContent(document, position);
+ return hoverContent ? new vscode.Hover(hoverContent) : null;
+ },
+ }
+ )
+ );
+ }
+
+ // hover content for detected smells
+ getHoverContent(document: vscode.TextDocument, position: vscode.Position): vscode.MarkdownString | null {
+ const lineNumber = position.line + 1; // convert to 1-based index
+
+ // filter to find the smells on current line
+ const smellsOnLine = this.smells.filter((smell) =>
+ smell.occurences.some((occurrence) =>
+ occurrence.line === lineNumber ||
+ (occurrence.endLine && lineNumber >= occurrence.line && lineNumber <= occurrence.endLine)
+ )
+ );
+
+ if (smellsOnLine.length === 0) {
+ return null;
+ }
+
+ const hoverContent = new vscode.MarkdownString();
+ hoverContent.isTrusted = true; // Allow command links
+
+ smellsOnLine.forEach((smell) => {
+ hoverContent.appendMarkdown(
+ `**${smell.symbol}:** ${smell.message}\t\t` +
+ `[Refactor](command:extension.refactorThisSmell?${encodeURIComponent(
+ JSON.stringify(smell)
+ )})\n\n`
+ );
+ });
+
+ hoverContent.appendMarkdown(
+ `---\n\n[Refactor all smells of this type...](command:extension.refactorSmellTypeDropdown?${encodeURIComponent(
+ JSON.stringify(smellsOnLine)
+ )})\n\n`
+ );
+
+ return hoverContent;
+ }
+
+ // Register commands for refactor actions
+ private registerCommands(): void {
+ this.vscodeContext.subscriptions.push(
+ vscode.commands.registerCommand(
+ "extension.refactorThisSmell",
+ async (smell: Smell) => {
+ const contextManager = new ContextManager(this.vscodeContext);
+ await refactorSelectedSmell(contextManager, this.vscodeContext);
+ }
+ ),
+ // clicking "Refactor All Smells of this Type..."
+ vscode.commands.registerCommand(
+ "extension.refactorSmellTypeDropdown",
+ async (smellsOnLine: Smell[]) => {
+ const smellTypes = Array.from(
+ new Set(smellsOnLine.map((smell) => smell.type))
+ );
+ const selectedSmellType = await vscode.window.showQuickPick(smellTypes, {
+ placeHolder: "Select a smell type to refactor across the repository",
+ });
+
+ if (selectedSmellType) {
+ vscode.commands.executeCommand(
+ "extension.refactorSmellAcrossRepo",
+ selectedSmellType
+ );
+ }
+ }
+ ),
+ // clicking the specific smell from dropdown
+ vscode.commands.registerCommand(
+ "extension.refactorSmellAcrossRepo",
+ (smellType: string) => {
+ vscode.window.showInformationMessage(
+ `Refactoring all occurrences of: ${smellType}`
+ );
+ const refactorSmells = require("../commands/refactorSmell");
+ refactorSmells.refactorAllOfType(smellType);
+ }
+ )
+ );
+ }
+}
From c3e1cde3a8c0fbd154c4c65646a2d33cadeef40c Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Thu, 30 Jan 2025 14:46:37 -0500
Subject: [PATCH 12/83] changed refactorSmell.ts to refactor selected smell
from hover Window
---
src/commands/refactorSmell.ts | 46 ++++++++++++++++++++++++-----------
src/ui/hoverManager.ts | 2 +-
2 files changed, 33 insertions(+), 15 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 99916c0..fb670cd 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -7,7 +7,6 @@ import { Smell } from '../types';
import * as fs from 'fs';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
-// import { HoverManager } from '../ui/hoverManager';
async function refactorLine(
@@ -16,6 +15,9 @@ async function refactorLine(
contextManager: ContextManager
) {
try {
+ vscode.window.showInformationMessage(
+ `Eco: Smell ID ${smell.messageId} on line ${smell.occurences[0].line}`
+ );
const refactorResult = await refactorSmell(filePath, smell);
return refactorResult;
} catch (error) {
@@ -25,7 +27,7 @@ async function refactorLine(
}
}
-export async function refactorSelectedSmell(contextManager: ContextManager, context: vscode.ExtensionContext) {
+export async function refactorSelectedSmell(contextManager: ContextManager, context: vscode.ExtensionContext, smellId?: string) {
const { editor, filePath } = getEditorAndFilePath();
if (!editor) {
@@ -57,31 +59,47 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
return;
}
- const matchingSmells = smellsData.filter((smell: Smell) => {
- return selectedLine === smell.occurences[0].line;
- });
- console.log(JSON.stringify(matchingSmells));
- console.log('===========================================================');
- if (matchingSmells.length === 0) {
+
+ // If smellId is provided, find that specific smell
+ let smellToRefactor: Smell | undefined;
+ if (smellId) {
vscode.window.showInformationMessage(
- 'Eco: Selected line(s) does not include a refactorable code pattern. Please switch to a line with highlighted code smell.'
+ `Eco: Smell ID ${smellId}`
);
- return;
+ smellToRefactor = smellsData.find((smell: Smell) => smell.messageId === smellId);
+ if (!smellToRefactor) {
+ vscode.window.showErrorMessage(
+ `Eco: Could not find smell with ID ${smellId}`
+ );
+ return;
+ }
+ } else {
+ // Original line-based logic as fallback
+ const matchingSmells = smellsData.filter((smell: Smell) => {
+ return selectedLine === smell.occurences[0].line;
+ });
+ if (matchingSmells.length === 0) {
+ vscode.window.showInformationMessage(
+ 'Eco: Selected line(s) does not include a refactorable code pattern. Please switch to a line with highlighted code smell.'
+ );
+ return;
+ }
+ smellToRefactor = matchingSmells[0];
}
vscode.window.showInformationMessage(
- 'Eco: Refactoring smell on selected line.'
+ 'Eco: Refactoring selected smell.'
);
console.log('Detecting smells in detectSmells on selected line');
- //refactor the first found smell
- //TODO UI that allows users to choose the smell to refactor
+ //refactor the selected smell
const refactorResult = await refactorLine(
- matchingSmells[0],
+ smellToRefactor,
filePath,
contextManager
);
+
if (!refactorResult) {
vscode.window.showErrorMessage(
'Eco: Refactoring failed. See console for details.'
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index 0fd6c19..752e4d4 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -87,7 +87,7 @@ export class HoverManager {
"extension.refactorThisSmell",
async (smell: Smell) => {
const contextManager = new ContextManager(this.vscodeContext);
- await refactorSelectedSmell(contextManager, this.vscodeContext);
+ await refactorSelectedSmell(contextManager, this.vscodeContext, smell.messageId);
}
),
// clicking "Refactor All Smells of this Type..."
From 509480ea0b705067ec01f8adc8bda54b41ad3ded Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Thu, 30 Jan 2025 15:07:35 -0500
Subject: [PATCH 13/83] Fixed smells showing up multiple times in the same
hover window
---
src/ui/fileHighlighter.ts | 3 +--
src/ui/hoverManager.ts | 44 ++++++++++++++++++++++++++++++---------
2 files changed, 35 insertions(+), 12 deletions(-)
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index 212084a..73323f0 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -67,8 +67,7 @@ export class FileHighlighter {
const range = new vscode.Range(line, indexStart, line, indexEnd);
const hoverManager = HoverManager.getInstance(this.contextManager, smells);
- const hoverContent = hoverManager.getHoverContent(editor.document, new vscode.Position(line, indexStart));
- return { range, hoverMessage: hoverContent || undefined }; // option to hover over and read smell details
+ return { range, hoverMessage: hoverManager.hoverContent || undefined }; // option to hover over and read smell details
});
this.decoration = aLittleExtra;
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index 752e4d4..0d08756 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -6,6 +6,7 @@ import { ContextManager } from "../context/contextManager";
export class HoverManager {
private static instance: HoverManager;
private smells: Smell[];
+ public hoverContent: vscode.MarkdownString;
private vscodeContext: vscode.ExtensionContext;
static getInstance(contextManager: ContextManager, smells: Smell[]): HoverManager {
@@ -20,7 +21,7 @@ export class HoverManager {
private constructor(private contextManager: ContextManager, smells: Smell[]) {
this.smells = smells || [];
this.vscodeContext = contextManager.context;
- this.registerHoverProvider();
+ this.hoverContent = this.registerHoverProvider() ?? new vscode.MarkdownString();
this.registerCommands();
}
@@ -45,6 +46,7 @@ export class HoverManager {
// hover content for detected smells
getHoverContent(document: vscode.TextDocument, position: vscode.Position): vscode.MarkdownString | null {
+
const lineNumber = position.line + 1; // convert to 1-based index
// filter to find the smells on current line
@@ -94,18 +96,40 @@ export class HoverManager {
vscode.commands.registerCommand(
"extension.refactorSmellTypeDropdown",
async (smellsOnLine: Smell[]) => {
- const smellTypes = Array.from(
- new Set(smellsOnLine.map((smell) => smell.type))
+ vscode.window.showInformationMessage(
+ `Eco: clicked refactorSmellTypeDropdown`
);
- const selectedSmellType = await vscode.window.showQuickPick(smellTypes, {
- placeHolder: "Select a smell type to refactor across the repository",
- });
+ const hoverContent = new vscode.MarkdownString();
+ hoverContent.isTrusted = true;
- if (selectedSmellType) {
- vscode.commands.executeCommand(
- "extension.refactorSmellAcrossRepo",
- selectedSmellType
+ smellsOnLine.forEach((smell) => {
+ vscode.window.showInformationMessage(
+ `Eco: adding smell type ${smell.type}`
);
+ hoverContent.appendMarkdown(
+ `**${smell.type}**\n` +
+ `[Refactor all ${smell.type} smells](command:extension.refactorSmellAcrossRepo?${encodeURIComponent(
+ JSON.stringify(smell.type)
+ )})\n\n`
+ );
+ });
+
+ vscode.window.showInformationMessage(
+ `Eco: Smell type dropdown made ${hoverContent}`
+ );
+
+ // Show hover content
+ const editor = vscode.window.activeTextEditor;
+ if (editor) {
+ const position = editor.selection.active;
+ vscode.languages.registerHoverProvider('*', {
+ provideHover() {
+ vscode.window.showInformationMessage(
+ `Eco: Smell type dropdown made`
+ );
+ return new vscode.Hover(hoverContent);
+ }
+ });
}
}
),
From c9a942d53ce9b9ecd569402c34210705c884b9a6 Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Thu, 30 Jan 2025 15:09:41 -0500
Subject: [PATCH 14/83] fixed extension.ts for refactorSmell() call
---
src/extension.ts | 60 +++++++++++++++++++++++++++++-------------------
1 file changed, 37 insertions(+), 23 deletions(-)
diff --git a/src/extension.ts b/src/extension.ts
index 7d8b511..b8d18f0 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -75,11 +75,11 @@ export function activate(context: vscode.ExtensionContext) {
// ===============================================================
// Adds comments to lines describing the smell
- const lineSelectManager = new LineSelectionManager(contextManager);
context.subscriptions.push(
vscode.window.onDidChangeTextEditorSelection((event) => {
console.log(`Detected event: ${event.kind?.toString()}`);
+ const lineSelectManager = new LineSelectionManager(contextManager);
lineSelectManager.commentLine(event.textEditor);
})
);
@@ -87,22 +87,41 @@ export function activate(context: vscode.ExtensionContext) {
// Updates directory of file states (for checking if modified)
context.subscriptions.push(
vscode.workspace.onDidSaveTextDocument(async (document) => {
- await updateHash(contextManager, document);
+ const lastSavedHashes = contextManager.getWorkspaceData(
+ envConfig.FILE_CHANGES_KEY!,
+ {}
+ );
+ const lastHash = lastSavedHashes[document.fileName];
+ const currentHash = hashContent(document.getText());
+
+ if (lastHash !== undefined && lastHash !== currentHash) {
+ console.log(
+ `Document ${document.uri.fsPath} has changed since last save.`
+ );
+ }
+
+ // Update the hash in workspace storage
+ await updateLastSavedHash(contextManager, document);
})
);
- // Handles case of documents already being open on vscode open
- vscode.window.visibleTextEditors.forEach(async (editor) => {
- if (editor.document) {
- await updateHash(contextManager, editor.document);
- }
- });
-
// Initializes first state of document when opened while extension active
context.subscriptions.push(
- vscode.workspace.onDidOpenTextDocument(
- async (document) => await updateHash(contextManager, document)
- )
+ vscode.workspace.onDidOpenTextDocument(async (document) => {
+ console.log('Detected document opening');
+ const HASH_STORAGE_KEY = envConfig.FILE_CHANGES_KEY!;
+ const lastSavedHashes = contextManager.getWorkspaceData(
+ HASH_STORAGE_KEY,
+ {}
+ );
+ const lastHash = lastSavedHashes[document.fileName];
+ if (!lastHash) {
+ console.log(
+ `Saving current state of ${document.uri.fsPath.split('/').at(-1)}.`
+ );
+ await updateLastSavedHash(contextManager, document);
+ }
+ })
);
}
@@ -120,7 +139,7 @@ export function hashContent(content: string): string {
}
// Function to update the stored hashes in workspace storage
-async function updateHash(
+async function updateLastSavedHash(
contextManager: ContextManager,
document: vscode.TextDocument
) {
@@ -128,15 +147,10 @@ async function updateHash(
envConfig.FILE_CHANGES_KEY!,
{}
);
- const lastHash = lastSavedHashes[document.fileName];
const currentHash = hashContent(document.getText());
-
- if (lastHash !== undefined && lastHash !== currentHash) {
- console.log(`Document ${document.uri.fsPath} has changed since last save.`);
- lastSavedHashes[document.fileName] = currentHash;
- await contextManager.setWorkspaceData(
- envConfig.FILE_CHANGES_KEY!,
- lastSavedHashes
- );
- }
+ lastSavedHashes[document.fileName] = currentHash;
+ await contextManager.setWorkspaceData(
+ envConfig.FILE_CHANGES_KEY!,
+ lastSavedHashes
+ );
}
From 27e914651ad478d01120b078bcfd72e510ed7614 Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Thu, 30 Jan 2025 16:10:42 -0500
Subject: [PATCH 15/83] refactor all smells of type button works
---
src/commands/refactorSmell.ts | 100 ++++++++++++++++++++++++++++++++--
src/ui/hoverManager.ts | 63 +++------------------
2 files changed, 103 insertions(+), 60 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index fb670cd..ba98431 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -51,6 +51,7 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
const smellsData: Smell[] = contextManager.getWorkspaceData(
envConfig.SMELL_MAP_KEY!
)[filePath].smells;
+
if (!smellsData || smellsData.length === 0) {
vscode.window.showErrorMessage(
'Eco: No smells detected in the file for refactoring.'
@@ -59,8 +60,6 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
return;
}
-
-
// If smellId is provided, find that specific smell
let smellToRefactor: Smell | undefined;
if (smellId) {
@@ -88,9 +87,6 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
smellToRefactor = matchingSmells[0];
}
- vscode.window.showInformationMessage(
- 'Eco: Refactoring selected smell.'
- );
console.log('Detecting smells in detectSmells on selected line');
//refactor the selected smell
@@ -108,6 +104,7 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
}
const { refactoredData, updatedSmells } = refactorResult;
+
if (!refactoredData) {
vscode.window.showErrorMessage(
'Eco: Refactoring failed. See console for details.'
@@ -140,3 +137,96 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
);
}
}
+
+export async function refactorAllSmellsOfType(contextManager: ContextManager, context: vscode.ExtensionContext, smellId: string) {
+ const { editor, filePath } = getEditorAndFilePath();
+
+ if (!editor) {
+ vscode.window.showErrorMessage(
+ 'Eco: Unable to proceed as no active editor found.'
+ );
+ console.log('No active editor found to refactor smell. Returning back.');
+ return;
+ }
+ if (!filePath) {
+ vscode.window.showErrorMessage(
+ 'Eco: Unable to proceed as active editor does not have a valid file path.'
+ );
+ console.log('No valid file path found to refactor smell. Returning back.');
+ return;
+ }
+
+ // only account for one selection to be refactored for now
+ const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
+
+ const smellsData: Smell[] = contextManager.getWorkspaceData(
+ envConfig.SMELL_MAP_KEY!
+ )[filePath].smells;
+
+ if (!smellsData || smellsData.length === 0) {
+ vscode.window.showErrorMessage(
+ 'Eco: No smells detected in the file for refactoring.'
+ );
+ console.log('No smells found in the file for refactoring.');
+ return;
+ }
+
+ // Filter smells by the given type ID
+ const smellsOfType = smellsData.filter((smell: Smell) => smell.messageId === smellId);
+
+ if (smellsOfType.length === 0) {
+ vscode.window.showWarningMessage(
+ `Eco: No smells of type ${smellId} found in the file.`
+ );
+ return;
+ }
+
+ let combinedRefactoredData = '';
+ let totalEnergySaved = 0;
+ let allUpdatedSmells: Smell[] = [];
+
+ // Refactor each smell of the given type
+ for (const smell of smellsOfType) {
+ const refactorResult = await refactorLine(smell, filePath, contextManager);
+
+ if (refactorResult && refactorResult.refactoredData) {
+ // Add two newlines between each refactored result
+ if (combinedRefactoredData) {
+ combinedRefactoredData += '\n\n';
+ }
+
+ fs.readFile(refactorResult.refactoredData.targetFile, (err, data) => {
+ if (!err) {
+ combinedRefactoredData += data.toString('utf8');
+ }
+ });
+
+ totalEnergySaved += refactorResult.refactoredData.energySaved;
+
+ if (refactorResult.updatedSmells) {
+ allUpdatedSmells = [...allUpdatedSmells, ...refactorResult.updatedSmells];
+ }
+ }
+ }
+
+ if (combinedRefactoredData) {
+ await RefactorManager.previewRefactor(editor, combinedRefactoredData);
+ vscode.window.showInformationMessage(
+ `Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(4)}`
+ );
+ } else {
+ vscode.window.showErrorMessage(
+ 'Eco: Refactoring failed. See console for details.'
+ );
+ return;
+ }
+
+ if (allUpdatedSmells.length) {
+ const fileHighlighter = new FileHighlighter(contextManager);
+ fileHighlighter.highlightSmells(editor, allUpdatedSmells);
+ } else {
+ vscode.window.showWarningMessage(
+ 'Eco: No updated smells detected after refactoring.'
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index 0d08756..d17178f 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { Smell } from "../types";
-import { refactorSelectedSmell } from "../commands/refactorSmell";
+import { refactorSelectedSmell, refactorAllSmellsOfType } from "../commands/refactorSmell";
import { ContextManager } from "../context/contextManager";
export class HoverManager {
@@ -69,16 +69,13 @@ export class HoverManager {
`**${smell.symbol}:** ${smell.message}\t\t` +
`[Refactor](command:extension.refactorThisSmell?${encodeURIComponent(
JSON.stringify(smell)
+ )})\t\t` +
+ `---[Refactor all smells of this type...](command:extension.refactorAllSmellsOfType?${encodeURIComponent(
+ JSON.stringify(smell)
)})\n\n`
);
});
- hoverContent.appendMarkdown(
- `---\n\n[Refactor all smells of this type...](command:extension.refactorSmellTypeDropdown?${encodeURIComponent(
- JSON.stringify(smellsOnLine)
- )})\n\n`
- );
-
return hoverContent;
}
@@ -94,56 +91,12 @@ export class HoverManager {
),
// clicking "Refactor All Smells of this Type..."
vscode.commands.registerCommand(
- "extension.refactorSmellTypeDropdown",
- async (smellsOnLine: Smell[]) => {
- vscode.window.showInformationMessage(
- `Eco: clicked refactorSmellTypeDropdown`
- );
- const hoverContent = new vscode.MarkdownString();
- hoverContent.isTrusted = true;
-
- smellsOnLine.forEach((smell) => {
- vscode.window.showInformationMessage(
- `Eco: adding smell type ${smell.type}`
- );
- hoverContent.appendMarkdown(
- `**${smell.type}**\n` +
- `[Refactor all ${smell.type} smells](command:extension.refactorSmellAcrossRepo?${encodeURIComponent(
- JSON.stringify(smell.type)
- )})\n\n`
- );
- });
-
- vscode.window.showInformationMessage(
- `Eco: Smell type dropdown made ${hoverContent}`
- );
-
- // Show hover content
- const editor = vscode.window.activeTextEditor;
- if (editor) {
- const position = editor.selection.active;
- vscode.languages.registerHoverProvider('*', {
- provideHover() {
- vscode.window.showInformationMessage(
- `Eco: Smell type dropdown made`
- );
- return new vscode.Hover(hoverContent);
- }
- });
- }
+ "extension.refactorAllSmellsOfType",
+ async (smell: Smell) => {
+ const contextManager = new ContextManager(this.vscodeContext);
+ await refactorAllSmellsOfType(contextManager, this.vscodeContext, smell.messageId);
}
),
- // clicking the specific smell from dropdown
- vscode.commands.registerCommand(
- "extension.refactorSmellAcrossRepo",
- (smellType: string) => {
- vscode.window.showInformationMessage(
- `Refactoring all occurrences of: ${smellType}`
- );
- const refactorSmells = require("../commands/refactorSmell");
- refactorSmells.refactorAllOfType(smellType);
- }
- )
);
}
}
From 0c4788b8b31bac6ebf8026e5c2546de2b732980e Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 30 Jan 2025 16:52:03 -0500
Subject: [PATCH 16/83] Moved hashing to utils and added wipe cache cmd
---
package.json | 5 ++
src/commands/wipeWorkCache.ts | 16 +++++++
src/extension.ts | 83 +++++++++++-----------------------
src/ui/lineSelectionManager.ts | 2 +-
src/utils/hashDocs.ts | 31 +++++++++++++
5 files changed, 79 insertions(+), 58 deletions(-)
create mode 100644 src/commands/wipeWorkCache.ts
create mode 100644 src/utils/hashDocs.ts
diff --git a/package.json b/package.json
index bc42a93..7cc6048 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,11 @@
"command": "ecooptimizer-vs-code-plugin.refactorSmell",
"title": "Refactor Smell",
"category": "Eco"
+ },
+ {
+ "command": "ecooptimizer-vs-code-plugin.wipeWorkCache",
+ "title": "Wipe Workspace Cache",
+ "category": "Eco"
}
],
"keybindings": [
diff --git a/src/commands/wipeWorkCache.ts b/src/commands/wipeWorkCache.ts
new file mode 100644
index 0000000..3e994d6
--- /dev/null
+++ b/src/commands/wipeWorkCache.ts
@@ -0,0 +1,16 @@
+import * as vscode from 'vscode';
+
+import { ContextManager } from '../context/contextManager';
+import { envConfig } from '../utils/envConfig';
+import { updateHash } from '../utils/hashDocs';
+
+export async function wipeWorkCache(contextManager: ContextManager) {
+ contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, {});
+ contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, {});
+
+ vscode.window.visibleTextEditors.forEach(async (editor) => {
+ if (editor.document) {
+ await updateHash(contextManager, editor.document);
+ }
+ });
+}
diff --git a/src/extension.ts b/src/extension.ts
index b8d18f0..35145c3 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -4,8 +4,9 @@ import * as vscode from 'vscode';
import { detectSmells } from './commands/detectSmells';
import { refactorSelectedSmell } from './commands/refactorSmell';
import { LineSelectionManager } from './ui/lineSelectionManager';
-import * as crypto from 'crypto';
import { ContextManager } from './context/contextManager';
+import { wipeWorkCache } from './commands/wipeWorkCache';
+import { updateHash } from './utils/hashDocs';
interface Smell {
line: number; // Known attribute
@@ -70,6 +71,19 @@ export function activate(context: vscode.ExtensionContext) {
);
context.subscriptions.push(refactorSmellCmd);
+ // Register Wipe Workspace Cache
+ let wipeWorkCacheCmd = vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.wipeWorkCache',
+ () => {
+ console.log('Command wipeWorkCache triggered');
+ vscode.window.showInformationMessage(
+ 'Eco: Wiping existing worspace memory...'
+ );
+ wipeWorkCache(contextManager);
+ }
+ );
+ context.subscriptions.push(wipeWorkCacheCmd);
+
// ===============================================================
// ADD LISTENERS
// ===============================================================
@@ -87,70 +101,25 @@ export function activate(context: vscode.ExtensionContext) {
// Updates directory of file states (for checking if modified)
context.subscriptions.push(
vscode.workspace.onDidSaveTextDocument(async (document) => {
- const lastSavedHashes = contextManager.getWorkspaceData(
- envConfig.FILE_CHANGES_KEY!,
- {}
- );
- const lastHash = lastSavedHashes[document.fileName];
- const currentHash = hashContent(document.getText());
-
- if (lastHash !== undefined && lastHash !== currentHash) {
- console.log(
- `Document ${document.uri.fsPath} has changed since last save.`
- );
- }
-
- // Update the hash in workspace storage
- await updateLastSavedHash(contextManager, document);
+ await updateHash(contextManager, document);
})
);
+ // Handles case of documents already being open on vscode open
+ vscode.window.visibleTextEditors.forEach(async (editor) => {
+ if (editor.document) {
+ await updateHash(contextManager, editor.document);
+ }
+ });
+
// Initializes first state of document when opened while extension active
context.subscriptions.push(
- vscode.workspace.onDidOpenTextDocument(async (document) => {
- console.log('Detected document opening');
- const HASH_STORAGE_KEY = envConfig.FILE_CHANGES_KEY!;
- const lastSavedHashes = contextManager.getWorkspaceData(
- HASH_STORAGE_KEY,
- {}
- );
- const lastHash = lastSavedHashes[document.fileName];
- if (!lastHash) {
- console.log(
- `Saving current state of ${document.uri.fsPath.split('/').at(-1)}.`
- );
- await updateLastSavedHash(contextManager, document);
- }
- })
+ vscode.workspace.onDidOpenTextDocument(
+ async (document) => await updateHash(contextManager, document)
+ )
);
}
export function deactivate() {
console.log('Refactor Plugin deactivated');
}
-
-// ===============================================================
-// UTILITY FUNCTIONS
-// ===============================================================
-
-// Function to hash the document content
-export function hashContent(content: string): string {
- return crypto.createHash('sha256').update(content).digest('hex');
-}
-
-// Function to update the stored hashes in workspace storage
-async function updateLastSavedHash(
- contextManager: ContextManager,
- document: vscode.TextDocument
-) {
- const lastSavedHashes = contextManager.getWorkspaceData(
- envConfig.FILE_CHANGES_KEY!,
- {}
- );
- const currentHash = hashContent(document.getText());
- lastSavedHashes[document.fileName] = currentHash;
- await contextManager.setWorkspaceData(
- envConfig.FILE_CHANGES_KEY!,
- lastSavedHashes
- );
-}
diff --git a/src/ui/lineSelectionManager.ts b/src/ui/lineSelectionManager.ts
index 85b1087..0dec43a 100644
--- a/src/ui/lineSelectionManager.ts
+++ b/src/ui/lineSelectionManager.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
import { SmellDetectRecord } from '../commands/detectSmells';
-import { hashContent } from '../extension';
+import { hashContent } from '../utils/hashDocs';
export class LineSelectionManager {
private contextManager;
diff --git a/src/utils/hashDocs.ts b/src/utils/hashDocs.ts
new file mode 100644
index 0000000..50f7114
--- /dev/null
+++ b/src/utils/hashDocs.ts
@@ -0,0 +1,31 @@
+import crypto from 'crypto';
+import { ContextManager } from '../context/contextManager';
+import { envConfig } from './envConfig';
+import * as vscode from 'vscode';
+
+// Function to hash the document content
+export function hashContent(content: string): string {
+ return crypto.createHash('sha256').update(content).digest('hex');
+}
+
+// Function to update the stored hashes in workspace storage
+export async function updateHash(
+ contextManager: ContextManager,
+ document: vscode.TextDocument
+) {
+ const lastSavedHashes = contextManager.getWorkspaceData(
+ envConfig.FILE_CHANGES_KEY!,
+ {}
+ );
+ const lastHash = lastSavedHashes[document.fileName];
+ const currentHash = hashContent(document.getText());
+
+ if (!lastHash || lastHash !== currentHash) {
+ console.log(`Document ${document.uri.fsPath} has changed since last save.`);
+ lastSavedHashes[document.fileName] = currentHash;
+ await contextManager.setWorkspaceData(
+ envConfig.FILE_CHANGES_KEY!,
+ lastSavedHashes
+ );
+ }
+}
From 634b4bb9c67a70c86092ee8d237fbcc888d895e0 Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Thu, 30 Jan 2025 16:41:00 -0500
Subject: [PATCH 17/83] removed silly parameter
---
src/commands/detectSmells.ts | 2 +-
src/extension.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 5e43847..d8cfe2a 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -32,7 +32,7 @@ export async function getSmells(
}
}
-export async function detectSmells(contextManager: ContextManager, context: vscode.ExtensionContext) {
+export async function detectSmells(contextManager: ContextManager) {
const { editor, filePath } = getEditorAndFilePath();
if (!editor) {
diff --git a/src/extension.ts b/src/extension.ts
index 35145c3..2853c4e 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -55,7 +55,7 @@ export function activate(context: vscode.ExtensionContext) {
'ecooptimizer-vs-code-plugin.detectSmells',
async () => {
console.log('Command detectSmells triggered');
- detectSmells(contextManager, context);
+ detectSmells(contextManager);
}
);
context.subscriptions.push(detectSmellsCmd);
From a6a57020000de2a6daa7e9139b88adc1fbdf541a Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 30 Jan 2025 18:12:30 -0500
Subject: [PATCH 18/83] removed unnecessary parameter
---
src/commands/refactorSmell.ts | 43 +++++---
src/commands/wipeWorkCache.ts | 2 +
src/extension.ts | 2 +-
src/ui/hoverManager.ts | 186 ++++++++++++++++++----------------
4 files changed, 129 insertions(+), 104 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index ba98431..6901db4 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -8,7 +8,6 @@ import * as fs from 'fs';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
-
async function refactorLine(
smell: Smell,
filePath: string,
@@ -27,7 +26,10 @@ async function refactorLine(
}
}
-export async function refactorSelectedSmell(contextManager: ContextManager, context: vscode.ExtensionContext, smellId?: string) {
+export async function refactorSelectedSmell(
+ contextManager: ContextManager,
+ smellId?: string
+) {
const { editor, filePath } = getEditorAndFilePath();
if (!editor) {
@@ -51,7 +53,7 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
const smellsData: Smell[] = contextManager.getWorkspaceData(
envConfig.SMELL_MAP_KEY!
)[filePath].smells;
-
+
if (!smellsData || smellsData.length === 0) {
vscode.window.showErrorMessage(
'Eco: No smells detected in the file for refactoring.'
@@ -63,10 +65,10 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
// If smellId is provided, find that specific smell
let smellToRefactor: Smell | undefined;
if (smellId) {
- vscode.window.showInformationMessage(
- `Eco: Smell ID ${smellId}`
+ vscode.window.showInformationMessage(`Eco: Smell ID ${smellId}`);
+ smellToRefactor = smellsData.find(
+ (smell: Smell) => smell.messageId === smellId
);
- smellToRefactor = smellsData.find((smell: Smell) => smell.messageId === smellId);
if (!smellToRefactor) {
vscode.window.showErrorMessage(
`Eco: Could not find smell with ID ${smellId}`
@@ -104,7 +106,6 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
}
const { refactoredData, updatedSmells } = refactorResult;
-
if (!refactoredData) {
vscode.window.showErrorMessage(
'Eco: Refactoring failed. See console for details.'
@@ -138,7 +139,10 @@ export async function refactorSelectedSmell(contextManager: ContextManager, cont
}
}
-export async function refactorAllSmellsOfType(contextManager: ContextManager, context: vscode.ExtensionContext, smellId: string) {
+export async function refactorAllSmellsOfType(
+ contextManager: ContextManager,
+ smellId: string
+) {
const { editor, filePath } = getEditorAndFilePath();
if (!editor) {
@@ -162,7 +166,7 @@ export async function refactorAllSmellsOfType(contextManager: ContextManager, co
const smellsData: Smell[] = contextManager.getWorkspaceData(
envConfig.SMELL_MAP_KEY!
)[filePath].smells;
-
+
if (!smellsData || smellsData.length === 0) {
vscode.window.showErrorMessage(
'Eco: No smells detected in the file for refactoring.'
@@ -172,7 +176,9 @@ export async function refactorAllSmellsOfType(contextManager: ContextManager, co
}
// Filter smells by the given type ID
- const smellsOfType = smellsData.filter((smell: Smell) => smell.messageId === smellId);
+ const smellsOfType = smellsData.filter(
+ (smell: Smell) => smell.messageId === smellId
+ );
if (smellsOfType.length === 0) {
vscode.window.showWarningMessage(
@@ -188,13 +194,13 @@ export async function refactorAllSmellsOfType(contextManager: ContextManager, co
// Refactor each smell of the given type
for (const smell of smellsOfType) {
const refactorResult = await refactorLine(smell, filePath, contextManager);
-
+
if (refactorResult && refactorResult.refactoredData) {
// Add two newlines between each refactored result
if (combinedRefactoredData) {
combinedRefactoredData += '\n\n';
}
-
+
fs.readFile(refactorResult.refactoredData.targetFile, (err, data) => {
if (!err) {
combinedRefactoredData += data.toString('utf8');
@@ -202,9 +208,12 @@ export async function refactorAllSmellsOfType(contextManager: ContextManager, co
});
totalEnergySaved += refactorResult.refactoredData.energySaved;
-
+
if (refactorResult.updatedSmells) {
- allUpdatedSmells = [...allUpdatedSmells, ...refactorResult.updatedSmells];
+ allUpdatedSmells = [
+ ...allUpdatedSmells,
+ ...refactorResult.updatedSmells
+ ];
}
}
}
@@ -212,7 +221,9 @@ export async function refactorAllSmellsOfType(contextManager: ContextManager, co
if (combinedRefactoredData) {
await RefactorManager.previewRefactor(editor, combinedRefactoredData);
vscode.window.showInformationMessage(
- `Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(4)}`
+ `Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(
+ 4
+ )}`
);
} else {
vscode.window.showErrorMessage(
@@ -229,4 +240,4 @@ export async function refactorAllSmellsOfType(contextManager: ContextManager, co
'Eco: No updated smells detected after refactoring.'
);
}
-}
\ No newline at end of file
+}
diff --git a/src/commands/wipeWorkCache.ts b/src/commands/wipeWorkCache.ts
index 3e994d6..645ed81 100644
--- a/src/commands/wipeWorkCache.ts
+++ b/src/commands/wipeWorkCache.ts
@@ -13,4 +13,6 @@ export async function wipeWorkCache(contextManager: ContextManager) {
await updateHash(contextManager, editor.document);
}
});
+
+ vscode.window.showInformationMessage('Workspace cache clean!');
}
diff --git a/src/extension.ts b/src/extension.ts
index 2853c4e..2f37ff9 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -66,7 +66,7 @@ export function activate(context: vscode.ExtensionContext) {
() => {
console.log('Command refactorSmells triggered');
vscode.window.showInformationMessage('Eco: Detecting smells...');
- refactorSelectedSmell(contextManager, context);
+ refactorSelectedSmell(contextManager);
}
);
context.subscriptions.push(refactorSmellCmd);
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index d17178f..edde3f4 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -1,102 +1,114 @@
-import * as vscode from "vscode";
-import { Smell } from "../types";
-import { refactorSelectedSmell, refactorAllSmellsOfType } from "../commands/refactorSmell";
-import { ContextManager } from "../context/contextManager";
+import * as vscode from 'vscode';
+import { Smell } from '../types';
+import {
+ refactorSelectedSmell,
+ refactorAllSmellsOfType
+} from '../commands/refactorSmell';
+import { ContextManager } from '../context/contextManager';
export class HoverManager {
- private static instance: HoverManager;
- private smells: Smell[];
- public hoverContent: vscode.MarkdownString;
- private vscodeContext: vscode.ExtensionContext;
+ private static instance: HoverManager;
+ private smells: Smell[];
+ public hoverContent: vscode.MarkdownString;
+ private vscodeContext: vscode.ExtensionContext;
- static getInstance(contextManager: ContextManager, smells: Smell[]): HoverManager {
- if (!HoverManager.instance) {
- HoverManager.instance = new HoverManager(contextManager, smells);
- } else {
- HoverManager.instance.updateSmells(smells);
- }
- return HoverManager.instance;
+ static getInstance(
+ contextManager: ContextManager,
+ smells: Smell[]
+ ): HoverManager {
+ if (!HoverManager.instance) {
+ HoverManager.instance = new HoverManager(contextManager, smells);
+ } else {
+ HoverManager.instance.updateSmells(smells);
}
+ return HoverManager.instance;
+ }
- private constructor(private contextManager: ContextManager, smells: Smell[]) {
- this.smells = smells || [];
- this.vscodeContext = contextManager.context;
- this.hoverContent = this.registerHoverProvider() ?? new vscode.MarkdownString();
- this.registerCommands();
- }
+ private constructor(private contextManager: ContextManager, smells: Smell[]) {
+ this.smells = smells || [];
+ this.vscodeContext = contextManager.context;
+ this.hoverContent =
+ this.registerHoverProvider() ?? new vscode.MarkdownString();
+ this.registerCommands();
+ }
- private updateSmells(smells: Smell[]): void {
- this.smells = smells || [];
- }
+ private updateSmells(smells: Smell[]): void {
+ this.smells = smells || [];
+ }
- // Register hover provider for Python files
- private registerHoverProvider(): void {
- this.vscodeContext.subscriptions.push(
- vscode.languages.registerHoverProvider(
- { scheme: "file", language: "python" },
- {
- provideHover: (document, position, token) => {
- const hoverContent = this.getHoverContent(document, position);
- return hoverContent ? new vscode.Hover(hoverContent) : null;
- },
- }
- )
- );
- }
+ // Register hover provider for Python files
+ private registerHoverProvider(): void {
+ this.vscodeContext.subscriptions.push(
+ vscode.languages.registerHoverProvider(
+ { scheme: 'file', language: 'python' },
+ {
+ provideHover: (document, position, token) => {
+ const hoverContent = this.getHoverContent(document, position);
+ return hoverContent ? new vscode.Hover(hoverContent) : null;
+ }
+ }
+ )
+ );
+ }
- // hover content for detected smells
- getHoverContent(document: vscode.TextDocument, position: vscode.Position): vscode.MarkdownString | null {
-
- const lineNumber = position.line + 1; // convert to 1-based index
+ // hover content for detected smells
+ getHoverContent(
+ document: vscode.TextDocument,
+ position: vscode.Position
+ ): vscode.MarkdownString | null {
+ const lineNumber = position.line + 1; // convert to 1-based index
- // filter to find the smells on current line
- const smellsOnLine = this.smells.filter((smell) =>
- smell.occurences.some((occurrence) =>
- occurrence.line === lineNumber ||
- (occurrence.endLine && lineNumber >= occurrence.line && lineNumber <= occurrence.endLine)
- )
- );
+ // filter to find the smells on current line
+ const smellsOnLine = this.smells.filter((smell) =>
+ smell.occurences.some(
+ (occurrence) =>
+ occurrence.line === lineNumber ||
+ (occurrence.endLine &&
+ lineNumber >= occurrence.line &&
+ lineNumber <= occurrence.endLine)
+ )
+ );
- if (smellsOnLine.length === 0) {
- return null;
- }
+ if (smellsOnLine.length === 0) {
+ return null;
+ }
- const hoverContent = new vscode.MarkdownString();
- hoverContent.isTrusted = true; // Allow command links
+ const hoverContent = new vscode.MarkdownString();
+ hoverContent.isTrusted = true; // Allow command links
- smellsOnLine.forEach((smell) => {
- hoverContent.appendMarkdown(
- `**${smell.symbol}:** ${smell.message}\t\t` +
- `[Refactor](command:extension.refactorThisSmell?${encodeURIComponent(
- JSON.stringify(smell)
- )})\t\t` +
- `---[Refactor all smells of this type...](command:extension.refactorAllSmellsOfType?${encodeURIComponent(
- JSON.stringify(smell)
- )})\n\n`
- );
- });
+ smellsOnLine.forEach((smell) => {
+ hoverContent.appendMarkdown(
+ `**${smell.symbol}:** ${smell.message}\t\t` +
+ `[Refactor](command:extension.refactorThisSmell?${encodeURIComponent(
+ JSON.stringify(smell)
+ )})\t\t` +
+ `---[Refactor all smells of this type...](command:extension.refactorAllSmellsOfType?${encodeURIComponent(
+ JSON.stringify(smell)
+ )})\n\n`
+ );
+ });
- return hoverContent;
- }
+ return hoverContent;
+ }
- // Register commands for refactor actions
- private registerCommands(): void {
- this.vscodeContext.subscriptions.push(
- vscode.commands.registerCommand(
- "extension.refactorThisSmell",
- async (smell: Smell) => {
- const contextManager = new ContextManager(this.vscodeContext);
- await refactorSelectedSmell(contextManager, this.vscodeContext, smell.messageId);
- }
- ),
- // clicking "Refactor All Smells of this Type..."
- vscode.commands.registerCommand(
- "extension.refactorAllSmellsOfType",
- async (smell: Smell) => {
- const contextManager = new ContextManager(this.vscodeContext);
- await refactorAllSmellsOfType(contextManager, this.vscodeContext, smell.messageId);
- }
- ),
- );
- }
+ // Register commands for refactor actions
+ private registerCommands(): void {
+ this.vscodeContext.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'extension.refactorThisSmell',
+ async (smell: Smell) => {
+ const contextManager = new ContextManager(this.vscodeContext);
+ await refactorSelectedSmell(contextManager, smell.messageId);
+ }
+ ),
+ // clicking "Refactor All Smells of this Type..."
+ vscode.commands.registerCommand(
+ 'extension.refactorAllSmellsOfType',
+ async (smell: Smell) => {
+ const contextManager = new ContextManager(this.vscodeContext);
+ await refactorAllSmellsOfType(contextManager, smell.messageId);
+ }
+ )
+ );
+ }
}
From 4c2859e24e59f64234323c253e8e8b89987e1cbf Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 30 Jan 2025 18:30:52 -0500
Subject: [PATCH 19/83] added back fixes that were lost
---
src/extension.ts | 28 +++++++++-------------------
src/ui/lineSelectionManager.ts | 16 +++++++++-------
2 files changed, 18 insertions(+), 26 deletions(-)
diff --git a/src/extension.ts b/src/extension.ts
index 2f37ff9..625e93f 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,6 +1,7 @@
-import { envConfig } from './utils/envConfig';
+import { envConfig } from './utils/envConfig'; // ENV variables should always be first import!!
import * as vscode from 'vscode';
+
import { detectSmells } from './commands/detectSmells';
import { refactorSelectedSmell } from './commands/refactorSmell';
import { LineSelectionManager } from './ui/lineSelectionManager';
@@ -8,11 +9,6 @@ import { ContextManager } from './context/contextManager';
import { wipeWorkCache } from './commands/wipeWorkCache';
import { updateHash } from './utils/hashDocs';
-interface Smell {
- line: number; // Known attribute
- [key: string]: any; // Index signature for unknown properties
-}
-
export function activate(context: vscode.ExtensionContext) {
console.log('Refactor Plugin activated');
@@ -22,22 +18,16 @@ export function activate(context: vscode.ExtensionContext) {
// INITIALIZE WORKSPACE DATA
// ===============================================================
- let allDetectedSmells = contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!
- );
- let fileHashes = contextManager.getWorkspaceData(envConfig.FILE_CHANGES_KEY!);
-
- if (!allDetectedSmells) {
- allDetectedSmells = {};
- contextManager.setWorkspaceData(
- envConfig.SMELL_MAP_KEY!,
- allDetectedSmells
- );
+ let smellsData = contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!);
+ if (!smellsData) {
+ smellsData = {};
+ contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, smellsData);
}
+ let fileHashes = contextManager.getWorkspaceData(envConfig.FILE_CHANGES_KEY!);
if (!fileHashes) {
fileHashes = {};
- contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, fileHashes);
+ contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, fileHashes);
}
console.log(
@@ -89,11 +79,11 @@ export function activate(context: vscode.ExtensionContext) {
// ===============================================================
// Adds comments to lines describing the smell
+ const lineSelectManager = new LineSelectionManager(contextManager);
context.subscriptions.push(
vscode.window.onDidChangeTextEditorSelection((event) => {
console.log(`Detected event: ${event.kind?.toString()}`);
- const lineSelectManager = new LineSelectionManager(contextManager);
lineSelectManager.commentLine(event.textEditor);
})
);
diff --git a/src/ui/lineSelectionManager.ts b/src/ui/lineSelectionManager.ts
index 0dec43a..5e92a2f 100644
--- a/src/ui/lineSelectionManager.ts
+++ b/src/ui/lineSelectionManager.ts
@@ -22,6 +22,10 @@ export class LineSelectionManager {
public commentLine(editor: vscode.TextEditor) {
this.removeLastComment();
+ if (!editor) {
+ return;
+ }
+
const filePath = editor.document.fileName;
const smellsDetectRecord = this.contextManager.getWorkspaceData(
envConfig.SMELL_MAP_KEY!
@@ -57,7 +61,9 @@ export class LineSelectionManager {
let comment;
if (smellsAtLine.length > 1) {
- comment = `π Smell: ${smellsAtLine[0].symbol} | ...`;
+ comment = `π Smell: ${smellsAtLine[0].symbol} | (+${
+ smellsAtLine.length - 1
+ })`;
} else {
comment = `π Smell: ${smellsAtLine[0].symbol}`;
}
@@ -66,16 +72,12 @@ export class LineSelectionManager {
isWholeLine: true,
after: {
contentText: comment,
- color: 'rgb(153, 211, 212)', // Red-orange for visibility
- margin: '0 0 0 10px', // Moves it to the right edge
+ color: 'rgb(153, 211, 212)',
+ margin: '0 0 0 10px',
textDecoration: 'none'
}
});
- if (!editor) {
- return;
- }
-
const selectionLine: vscode.Range[] = [];
const line_text = editor.document.lineAt(selectedLine).text;
From 1159bb7b6fad6afa8e12c70ac904c83e98064f89 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 30 Jan 2025 19:39:24 -0500
Subject: [PATCH 20/83] fixed bug added multiple arrow decorations to multi
smell line
---
src/commands/detectSmells.ts | 12 ++++++++----
src/commands/refactorSmell.ts | 20 +++++++++++++++++++-
src/ui/fileHighlighter.ts | 24 +++++++++++++++++-------
3 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index d8cfe2a..5a8279d 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -8,12 +8,13 @@ import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
// import { HoverManager } from "../ui/hoverManager"; // Import the HoverManager
-
export interface SmellDetectRecord {
hash: string;
smells: Smell[];
}
+let fileHighlighter: FileHighlighter;
+
export async function getSmells(
filePath: string,
contextManager: ContextManager
@@ -110,9 +111,12 @@ export async function detectSmells(contextManager: ContextManager) {
`Eco: Detected ${smellsData.length} smells in the file.`
);
- const fileHighlighter = new FileHighlighter(contextManager);
+ if (!fileHighlighter) {
+ fileHighlighter = new FileHighlighter(contextManager);
+ }
// const hoverManager = new HoverManager(context, smellsData);
fileHighlighter.highlightSmells(editor, smellsData);
- vscode.window.showInformationMessage('Eco: Detected code smells have been highlighted.');
+ vscode.window.showInformationMessage(
+ 'Eco: Detected code smells have been highlighted.'
+ );
}
-
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 6901db4..0f0df27 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -7,6 +7,7 @@ import { Smell } from '../types';
import * as fs from 'fs';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
+import path from 'path';
async function refactorLine(
smell: Smell,
@@ -91,7 +92,10 @@ export async function refactorSelectedSmell(
console.log('Detecting smells in detectSmells on selected line');
- //refactor the selected smell
+ let tempDirPaths: string[] = [];
+
+ //refactor the first found smell
+ //TODO UI that allows users to choose the smell to refactor
const refactorResult = await refactorLine(
smellToRefactor,
filePath,
@@ -113,6 +117,20 @@ export async function refactorSelectedSmell(
return;
}
+ // tempDirPaths.push(refactoredData.tempDir);
+ // tempDirPaths.forEach((dirPath: string) => {
+ // fs.rm(
+ // dirPath,
+ // {
+ // recursive: true,
+ // force: true
+ // },
+ // (err, data) => {
+
+ // }
+ // );
+ // });
+
// Did not test this yet, but if it works need to change so that all modified files are displayed
// only shows the file where the smell was found
console.log(`target file: ${refactoredData.targetFile}`);
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index 73323f0..aa603b3 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -50,12 +50,19 @@ export class FileHighlighter {
}
});
+ const seenLines: number[] = [];
const smellLines: vscode.DecorationOptions[] = smells
- .filter((smell: Smell) =>
- smell.occurences.every((occurrence: { line: number }) =>
+ .filter((smell: Smell) => {
+ const valid = smell.occurences.every((occurrence: { line: number }) =>
isValidLine(occurrence.line)
- )
- )
+ );
+ if (valid && seenLines.includes(smell.occurences[0].line)) {
+ return false;
+ } else if (valid) {
+ seenLines.push(smell.occurences[0].line);
+ }
+ return valid;
+ })
.map((smell: Smell) => {
const line = smell.occurences[0].line - 1; // convert to zero-based line index for VS editor
@@ -66,9 +73,12 @@ export class FileHighlighter {
const range = new vscode.Range(line, indexStart, line, indexEnd);
- const hoverManager = HoverManager.getInstance(this.contextManager, smells);
- return { range, hoverMessage: hoverManager.hoverContent || undefined }; // option to hover over and read smell details
- });
+ const hoverManager = HoverManager.getInstance(
+ this.contextManager,
+ smells
+ );
+ return { range, hoverMessage: hoverManager.hoverContent || undefined }; // option to hover over and read smell details
+ });
this.decoration = aLittleExtra;
From dc0a6c974b6eb1a4c5269e597a86fe11d03df008 Mon Sep 17 00:00:00 2001
From: mya
Date: Fri, 31 Jan 2025 03:58:08 -0500
Subject: [PATCH 21/83] Added Refactoring comparison functionality closes #353
in ecooptimizer repo
---
src/commands/refactorSmell.ts | 122 ++++++---------------------
src/ui/diffViewer.ts | 150 +++++++++++++++-------------------
2 files changed, 90 insertions(+), 182 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 918c8ca..a21ae73 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -34,130 +34,56 @@ export async function refactorSelectedSmell(
) {
const { editor, filePath } = getEditorAndFilePath();
- if (!editor) {
- vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as no active editor found.'
- );
- console.log('No active editor found to refactor smell. Returning back.');
- return;
- }
- if (!filePath) {
- vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as active editor does not have a valid file path.'
- );
- console.log('No valid file path found to refactor smell. Returning back.');
+ if (!editor || !filePath) {
+ vscode.window.showErrorMessage('Eco: Unable to proceed as no active editor or file path found.');
return;
}
- // only account for one selection to be refactored for now
- const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
+ const selectedLine = editor.selection.start.line + 1; // Update to VS Code editor indexing
const smellsData: Smell[] = contextManager.getWorkspaceData(
envConfig.SMELL_MAP_KEY!
)[filePath].smells;
if (!smellsData || smellsData.length === 0) {
- vscode.window.showErrorMessage(
- 'Eco: No smells detected in the file for refactoring.'
- );
- console.log('No smells found in the file for refactoring.');
+ vscode.window.showErrorMessage('Eco: No smells detected in the file for refactoring.');
return;
}
- // If smellId is provided, find that specific smell
+ // Find the smell to refactor
let smellToRefactor: Smell | undefined;
if (smellId) {
- vscode.window.showInformationMessage(`Eco: Smell ID ${smellId}`);
- smellToRefactor = smellsData.find(
- (smell: Smell) => smell.messageId === smellId
- );
- if (!smellToRefactor) {
- vscode.window.showErrorMessage(
- `Eco: Could not find smell with ID ${smellId}`
- );
- return;
- }
+ smellToRefactor = smellsData.find((smell: Smell) => smell.messageId === smellId);
} else {
- // Original line-based logic as fallback
- const matchingSmells = smellsData.filter((smell: Smell) => {
- return selectedLine === smell.occurences[0].line;
- });
- if (matchingSmells.length === 0) {
- vscode.window.showInformationMessage(
- 'Eco: Selected line(s) does not include a refactorable code pattern. Please switch to a line with highlighted code smell.'
- );
- return;
- }
- smellToRefactor = matchingSmells[0];
+ smellToRefactor = smellsData.find((smell: Smell) => selectedLine === smell.occurences[0].line);
}
- console.log('Detecting smells in detectSmells on selected line');
-
- let tempDirPaths: string[] = [];
-
- //refactor the first found smell
- //TODO UI that allows users to choose the smell to refactor
- const refactorResult = await refactorLine(
- smellToRefactor,
- filePath,
- contextManager
- );
-
- if (!refactorResult) {
- vscode.window.showErrorMessage(
- 'Eco: Refactoring failed. See console for details.'
- );
+ if (!smellToRefactor) {
+ vscode.window.showErrorMessage('Eco: No matching smell found for refactoring.');
return;
}
- const { refactoredData, updatedSmells } = refactorResult;
- if (!refactoredData) {
- vscode.window.showErrorMessage(
- 'Eco: Refactoring failed. See console for details.'
- );
+ // Refactor the smell
+ const refactorResult = await refactorLine(smellToRefactor, filePath, contextManager);
+
+ if (!refactorResult || !refactorResult.refactoredData) {
+ vscode.window.showErrorMessage('Eco: Refactoring failed. See console for details.');
return;
}
- // tempDirPaths.push(refactoredData.tempDir);
- // tempDirPaths.forEach((dirPath: string) => {
- // fs.rm(
- // dirPath,
- // {
- // recursive: true,
- // force: true
- // },
- // (err, data) => {
+ const { refactoredData } = refactorResult;
- // }
- // );
- // });
+ //Read the refactored code
+ const refactoredCode = await fs.promises.readFile(refactoredData.targetFile, 'utf8');
- // Did not test this yet, but if it works need to change so that all modified files are displayed
- // only shows the file where the smell was found
- console.log(`target file: ${refactoredData.targetFile}`);
- fs.readFile(refactoredData.targetFile, async (err, data) => {
- if (err) {
- throw err;
- }
-
- // await RefactorManager.previewRefactor(editor, data.toString('utf8')); mya commented to test my difference library stuff
- await showDiffViewer(editor, data.toString('utf8'), "bumb");
- vscode.window.showInformationMessage(
- `Eco: Refactoring completed. Energy difference: ${refactoredData.energySaved.toFixed(
- 4
- )}`
- );
- });
+ //Get the original code from the editor
+ const originalCode = editor.document.getText();
- if (updatedSmells.length) {
- const fileHighlighter = new FileHighlighter(contextManager);
- // const hoverManager = new HoverManager(context, smellsData);
- fileHighlighter.highlightSmells(editor, updatedSmells);
- } else {
- vscode.window.showWarningMessage(
- 'Eco: No updated smells detected after refactoring.'
- );
- }
+ //Show the diff viewer
+ await showDiffViewer(editor, refactoredCode, originalCode);
+
+ // Clean up temporary files
+ await fs.promises.rm(refactoredData.tempDir, { recursive: true, force: true });
}
export async function refactorAllSmellsOfType(
diff --git a/src/ui/diffViewer.ts b/src/ui/diffViewer.ts
index bacc8db..5adafc5 100644
--- a/src/ui/diffViewer.ts
+++ b/src/ui/diffViewer.ts
@@ -1,104 +1,86 @@
import * as vscode from 'vscode';
-import { diffWords } from 'diff';
+import * as fs from 'fs';
+let statusBarItem: vscode.StatusBarItem | undefined; // Store globally
/**
- * Displays a WebView panel to show a visual diff between the original and refactored code.
+ * Displays a native VS Code diff view to compare the original and refactored code.
* Users can accept or reject the changes.
*/
export async function showDiffViewer(editor: vscode.TextEditor, refactoredCode: string, originalCode: string) {
- const panel = vscode.window.createWebviewPanel(
- 'ecoDiffViewer',
- 'Eco: Code Refactor Preview',
- vscode.ViewColumn.Two,
- { enableScripts: true }
- );
+ // Create temporary files for the original and refactored code
+ const originalUri = vscode.Uri.file(`${editor.document.fileName}.eco-original`);
+ const refactoredUri = vscode.Uri.file(`${editor.document.fileName}.eco-refactored`);
- const diffHtml = generateDiffHtml(originalCode, refactoredCode);
-
- panel.webview.html = `
-
-
-
-
-
- Eco: Code Refactor Preview
-
-
-
- Refactoring Preview
-
-
${diffHtml.original}
-
${diffHtml.refactored}
-
-
-
-
-
-
-
- `;
-
- // Handle messages from WebView
- panel.webview.onDidReceiveMessage(
- (message) => {
- if (message.command === 'accept') {
- applyRefactoredCode(editor, refactoredCode);
- panel.dispose();
- } else if (message.command === 'reject') {
- panel.dispose();
- }
- },
- []
- );
-}
+ // Write the original and refactored code to the temporary files
+ await vscode.workspace.fs.writeFile(originalUri, Buffer.from(originalCode));
+ await vscode.workspace.fs.writeFile(refactoredUri, Buffer.from(refactoredCode));
-/**
- * Generates side-by-side HTML diff highlighting differences.
- */
-function generateDiffHtml(original: string, refactored: string) {
- const diff = diffWords(original, refactored);
+ // Store a reference to the original editor
+ const originalEditor = editor;
+
+ // Show the diff view
+ await vscode.commands.executeCommand('vscode.diff', originalUri, refactoredUri, 'Eco: Code Refactor Preview');
+
+ // Remove previous status bar item if it exists
+ if (statusBarItem) {
+ statusBarItem.dispose();
+ }
+
+ // Create a new status bar item
+ statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
+ statusBarItem.text = 'β
Accept | β Reject';
+ statusBarItem.tooltip = 'Accept or reject the refactoring changes';
+ statusBarItem.command = 'eco.refactor.decision';
+ statusBarItem.show();
+
+ // Register a command to handle the user's decision
+ const disposable = vscode.commands.registerCommand('eco.refactor.decision', async () => {
+ const choice = await vscode.window.showQuickPick(['Accept', 'Reject'], {
+ placeHolder: 'Do you want to accept the refactoring changes?',
+ });
+
+ if (choice === 'Accept') {
+ // Get the actual original file path (without the .eco-original suffix)
+ const originalFileUri = vscode.Uri.file(editor.document.fileName);
- let originalHtml = '';
- let refactoredHtml = '';
+ // Close the diff preview
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
- diff.forEach((part) => {
- if (part.added) {
- refactoredHtml += `${part.value}`;
- } else if (part.removed) {
- originalHtml += `${part.value}`;
+ // Open the actual original file
+ const document = await vscode.workspace.openTextDocument(originalFileUri);
+ const originalEditor = await vscode.window.showTextDocument(document, { preview: false });
+
+ // Apply the refactored code to the actual file
+ await applyRefactoredCode(originalEditor, refactoredCode);
} else {
- originalHtml += part.value;
- refactoredHtml += part.value;
+ vscode.window.showInformationMessage('Refactoring changes rejected.');
+ // Close the diff preview
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
}
- });
- return { original: originalHtml, refactored: refactoredHtml };
+
+ // Clean up temporary files and hide the status bar item
+ await vscode.workspace.fs.delete(originalUri);
+ await vscode.workspace.fs.delete(refactoredUri);
+ statusBarItem?.hide();
+ disposable.dispose();
+});
+
+
}
/**
* Replaces the selected code in the editor with the refactored version.
*/
-function applyRefactoredCode(editor: vscode.TextEditor, newCode: string) {
- editor.edit((editBuilder) => {
- const fullRange = new vscode.Range(
- new vscode.Position(0, 0),
- new vscode.Position(editor.document.lineCount, 0)
- );
+async function applyRefactoredCode(editor: vscode.TextEditor, newCode: string) {
+ const fullRange = new vscode.Range(
+ new vscode.Position(0, 0),
+ new vscode.Position(editor.document.lineCount, 0)
+ );
+
+ await editor.edit((editBuilder) => {
editBuilder.replace(fullRange, newCode);
});
-}
+
+ vscode.window.showInformationMessage('Refactoring changes applied successfully.');
+}
\ No newline at end of file
From 61a39e7e3665c94e705ed0814dc1c498cd15c7c4 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 3 Feb 2025 04:27:22 -0500
Subject: [PATCH 22/83] Updated refactoring diff view
(ssm-lab/capstone--sco-vs-code-plugin#353)
---
.prettierrc | 4 +-
.vscode/settings.json | 1 +
media/script.js | 93 +++++++++++++++++++
media/style.css | 42 +++++++++
media/vscode.css | 91 ++++++++++++++++++
media/webview.html | 30 ++++++
package.json | 38 +++++++-
src/api/backend.ts | 4 -
src/commands/detectSmells.ts | 17 ++--
src/commands/refactorSmell.ts | 160 +++++++++++++++++++++++++-------
src/context/contextManager.ts | 8 +-
src/extension.ts | 52 +++++++++--
src/global.d.ts | 9 +-
src/types.ts | 17 +++-
src/ui/refactorView.ts | 155 +++++++++++++++++++++++++++++++
src/utils/handleEditorChange.ts | 118 +++++++++++++++++++++++
src/utils/hashDocs.ts | 1 +
tsconfig.json | 9 +-
18 files changed, 781 insertions(+), 68 deletions(-)
create mode 100644 media/script.js
create mode 100644 media/style.css
create mode 100644 media/vscode.css
create mode 100644 media/webview.html
create mode 100644 src/ui/refactorView.ts
create mode 100644 src/utils/handleEditorChange.ts
diff --git a/.prettierrc b/.prettierrc
index aa566c4..bad8f54 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,5 +1,7 @@
{
"singleQuote": true,
"endOfLine": "auto",
- "trailingComma": "none"
+ "trailingComma": "none",
+ "bracketSpacing": true,
+ "printWidth": 85
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7ad2f2c..1a5f10e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -11,6 +11,7 @@
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"cSpell.words": [
+ "ecooptimizer",
"occurences"
]
}
diff --git a/media/script.js b/media/script.js
new file mode 100644
index 0000000..14cd794
--- /dev/null
+++ b/media/script.js
@@ -0,0 +1,93 @@
+const vscode = acquireVsCodeApi();
+
+function updateWebView(data) {
+ // Hide "No refactoring in progress" message
+ document.getElementById('no-data').style.display = 'none';
+ document.getElementById('container').style.display = 'block';
+
+ // Update Energy Saved
+ document.getElementById(
+ 'energy'
+ ).textContent = `Energy Saved: ${data.energySaved.toExponential(3)} J`;
+
+ // Populate Target File
+ const targetFileList = document.getElementById('target-file-list');
+ targetFileList.innerHTML = '';
+ const targetFile = data.targetFile.refactored.replace(data.tempDir, '');
+ const li = document.createElement('li');
+ li.textContent = targetFile;
+ li.classList.add('clickable');
+ li.onclick = () => {
+ vscode.postMessage({
+ command: 'selectFile',
+ original: data.targetFile.original,
+ refactored: data.targetFile.refactored
+ });
+ };
+ targetFileList.appendChild(li);
+
+ // Populate Other Modified Files
+ const affectedFileList = document.getElementById('affected-file-list');
+ affectedFileList.innerHTML = '';
+ if (data.affectedFiles.length === 0) {
+ document.getElementById('other-files-head').style.display = 'none';
+ }
+ data.affectedFiles.forEach((file) => {
+ const li = document.createElement('li');
+ li.textContent = file.refactored.replace(data.tempDir, '');
+ li.classList.add('clickable');
+ li.onclick = () => {
+ vscode.postMessage({
+ command: 'selectFile',
+ original: file.original,
+ refactored: file.refactored
+ });
+ };
+ affectedFileList.appendChild(li);
+ });
+
+ // Save state in the webview
+ vscode.setState(data);
+}
+
+// Function to clear the UI (for when refactoring is done)
+function clearWebview() {
+ document.getElementById('energy').textContent = 'Energy Saved: --';
+ document.getElementById('target-file-list').innerHTML = '';
+ document.getElementById('affected-file-list').innerHTML = '';
+
+ document.getElementById('no-data').style.display = 'block';
+ document.getElementById('container').style.display = 'none';
+ vscode.setState(null); // Clear state
+}
+
+// Restore state when webview loads
+// window.addEventListener('DOMContentLoaded', () => {
+// const savedState = vscode.getState();
+// if (savedState) {
+// updateWebView(savedState);
+// }
+// });
+
+// Listen for extension messages
+window.addEventListener('message', (event) => {
+ if (event.data.command === 'update') {
+ updateWebView(event.data.data);
+ } else if (event.data.command === 'clear') {
+ clearWebview();
+ } else if (event.data.command === 'pause') {
+ document.getElementById('no-data').style.display = 'block';
+ document.getElementById('container').style.display = 'none';
+ }
+});
+
+// Button click handlers
+document.getElementById('accept-btn').addEventListener('click', () => {
+ vscode.postMessage({ command: 'accept' });
+ clearWebview();
+});
+
+document.getElementById('reject-btn').addEventListener('click', () => {
+ vscode.postMessage({ command: 'reject' });
+ clearWebview();
+});
diff --git a/media/style.css b/media/style.css
new file mode 100644
index 0000000..2ce8d4e
--- /dev/null
+++ b/media/style.css
@@ -0,0 +1,42 @@
+body {
+ font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans',
+ Arial, sans-serif;
+ padding: 10px;
+}
+
+/* .body-text {
+ font-size: medium;
+} */
+.clickable {
+ cursor: pointer;
+ padding: 5px;
+ transition: background-color 0.2s ease-in-out;
+}
+.clickable:hover {
+ background-color: rgba(87, 82, 82, 0.1);
+}
+#container {
+ display: none; /* Initially hidden until data is received */
+}
+#no-data {
+ /* font-size: 14px; */
+ text-align: center;
+}
+#buttons {
+ position: absolute;
+ display: flex;
+ justify-content: space-between;
+ margin: 0 5px;
+ bottom: 10px;
+}
+ul {
+ overflow-y: auto;
+ padding: 0;
+ list-style-type: square;
+}
+
+button {
+ width: 45vw;
+ height: 40px;
+ border-radius: 2px;
+}
diff --git a/media/vscode.css b/media/vscode.css
new file mode 100644
index 0000000..12d43b9
--- /dev/null
+++ b/media/vscode.css
@@ -0,0 +1,91 @@
+:root {
+ --container-paddding: 20px;
+ --input-padding-vertical: 6px;
+ --input-padding-horizontal: 4px;
+ --input-margin-vertical: 4px;
+ --input-margin-horizontal: 0;
+}
+
+body {
+ padding: 0 var(--container-paddding);
+ color: var(--vscode-foreground);
+ font-size: var(--vscode-font-size);
+ font-weight: var(--vscode-font-weight);
+ font-family: var(--vscode-font-family);
+ background-color: var(--vscode-editor-background);
+}
+
+ol,
+ul {
+ padding-left: var(--container-paddding);
+}
+
+body > *,
+form > * {
+ margin-block-start: var(--input-margin-vertical);
+ margin-block-end: var(--input-margin-vertical);
+}
+
+*:focus {
+ outline-color: var(--vscode-focusBorder) !important;
+}
+
+a {
+ color: var(--vscode-textLink-foreground);
+}
+
+a:hover,
+a:active {
+ color: var(--vscode-textLink-activeForeground);
+}
+
+code {
+ font-size: var(--vscode-editor-font-size);
+ font-family: var(--vscode-editor-font-family);
+}
+
+button {
+ border: none;
+ padding: var(--input-padding-vertical) var(--input-padding-horizontal);
+ width: 100%;
+ text-align: center;
+ outline: 1px solid transparent;
+ outline-offset: 2px !important;
+ color: var(--vscode-button-foreground);
+ background: var(--vscode-button-background);
+}
+
+button:hover {
+ cursor: pointer;
+ background: var(--vscode-button-hoverBackground);
+}
+
+button:focus {
+ outline-color: var(--vscode-focusBorder);
+}
+
+button.secondary {
+ color: var(--vscode-button-secondaryForeground);
+ background: var(--vscode-button-secondaryBackground);
+}
+
+button.secondary:hover {
+ background: var(--vscode-button-secondaryHoverBackground);
+}
+
+input:not([type='checkbox']),
+textarea {
+ display: block;
+ width: 100%;
+ border: none;
+ font-family: var(--vscode-font-family);
+ padding: var(--input-padding-vertical) var(--input-padding-horizontal);
+ color: var(--vscode-input-foreground);
+ outline-color: var(--vscode-input-border);
+ background-color: var(--vscode-input-background);
+}
+
+input::placeholder,
+textarea::placeholder {
+ color: var(--vscode-input-placeholderForeground);
+}
diff --git a/media/webview.html b/media/webview.html
new file mode 100644
index 0000000..11278d4
--- /dev/null
+++ b/media/webview.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+ Refactoring Navigator
+
+
+
+ Nothing to see here. If you are currently refactoring a file, make sure the diff view is selected.
+
+
+
Refactoring Summary
+
Energy Saved: --
+
Target File
+
+
Other Modified Files
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
index 7cc6048..d612712 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,24 @@
"command": "ecooptimizer-vs-code-plugin.wipeWorkCache",
"title": "Wipe Workspace Cache",
"category": "Eco"
+ },
+ {
+ "command": "ecooptimizer-vs-code-plugin.showRefactorSidebar",
+ "title": "Show Refactor Sidebar",
+ "category": "Eco",
+ "enablement": "false"
+ },
+ {
+ "command": "ecooptimizer-vs-code-plugin.pauseRefactorSidebar",
+ "title": "Pause Refactor Sidebar",
+ "category": "Eco",
+ "enablement": "false"
+ },
+ {
+ "command": "ecooptimizer-vs-code-plugin.clearRefactorSidebar",
+ "title": "Clear Refactor Sidebar",
+ "category": "Eco",
+ "enablement": "false"
}
],
"keybindings": [
@@ -37,7 +55,25 @@
"key": "ctrl+shift+r",
"when": "editorTextFocus && resourceExtname == '.py'"
}
- ]
+ ],
+ "viewsContainers": {
+ "activitybar": [
+ {
+ "id": "refactorSidebarContainer",
+ "title": "Refactoring",
+ "icon": "resources/refactor-icon.svg"
+ }
+ ]
+ },
+ "views": {
+ "refactorSidebarContainer": [
+ {
+ "id": "extension.refactorSidebar",
+ "name": "Refactoring Summary",
+ "type": "webview"
+ }
+ ]
+ }
},
"scripts": {
"vscode:prepublish": "npm run package",
diff --git a/src/api/backend.ts b/src/api/backend.ts
index fe20489..a2a4c66 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -13,7 +13,6 @@ export async function fetchSmells(filePath: string): Promise {
throw new Error(`Error fetching smells: ${response.statusText}`);
}
const smellsList = (await response.json()) as Smell[];
- smellsList.forEach((smell) => console.log(JSON.stringify(smell)));
return smellsList;
} catch (error) {
console.error('Error in getSmells:', error);
@@ -33,9 +32,6 @@ export async function refactorSmell(
smell
};
- console.log(`payload: ${JSON.stringify(payload)}`);
- console.log(`${smell.path}`);
-
try {
const response = await fetch(url, {
method: 'POST',
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 5a8279d..ebdfe7d 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -6,6 +6,7 @@ import { Smell } from '../types';
import { fetchSmells } from '../api/backend';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
+import { hashContent, updateHash } from '../utils/hashDocs';
// import { HoverManager } from "../ui/hoverManager"; // Import the HoverManager
export interface SmellDetectRecord {
@@ -15,10 +16,7 @@ export interface SmellDetectRecord {
let fileHighlighter: FileHighlighter;
-export async function getSmells(
- filePath: string,
- contextManager: ContextManager
-) {
+export async function getSmells(filePath: string, contextManager: ContextManager) {
try {
const smellsList: Smell[] = await fetchSmells(filePath);
if (smellsList.length === 0) {
@@ -63,9 +61,7 @@ export async function detectSmells(contextManager: ContextManager) {
const fileSmells = allSmells[filePath];
- const currentFileHash = contextManager.getWorkspaceData(
- envConfig.FILE_CHANGES_KEY!
- )[filePath];
+ const currentFileHash = hashContent(editor.document.getText());
// Function to handle the smells data retrieval and updating
async function fetchAndStoreSmells(): Promise {
@@ -73,9 +69,7 @@ export async function detectSmells(contextManager: ContextManager) {
if (!smellsData) {
console.log('No valid smells data found. Returning.');
- vscode.window.showErrorMessage(
- 'Eco: No smells are present in current file.'
- );
+ vscode.window.showErrorMessage('Eco: No smells are present in current file.');
return undefined; // Indicate failure to fetch smells
}
@@ -92,12 +86,14 @@ export async function detectSmells(contextManager: ContextManager) {
if (currentFileHash === fileSmells.hash) {
smellsData = fileSmells.smells;
} else {
+ console.log('Updating smells');
smellsData = await fetchAndStoreSmells();
if (!smellsData) {
return;
}
}
} else {
+ updateHash(contextManager, editor.document);
smellsData = await fetchAndStoreSmells();
if (!smellsData) {
return;
@@ -106,7 +102,6 @@ export async function detectSmells(contextManager: ContextManager) {
console.log('Saving smells to workspace data.');
- console.log('Detected smells data: ', smellsData);
vscode.window.showInformationMessage(
`Eco: Detected ${smellsData.length} smells in the file.`
);
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index a21ae73..0d0c761 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -1,14 +1,15 @@
import * as vscode from 'vscode';
-import { RefactorManager } from '../ui/refactorManager';
+import * as fs from 'fs';
+
+import { envConfig } from '../utils/envConfig';
+
import { getEditorAndFilePath } from '../utils/editorUtils';
-import { FileHighlighter } from '../ui/fileHighlighter';
import { refactorSmell } from '../api/backend';
-import { Smell } from '../types';
-import * as fs from 'fs';
+
+import { FileHighlighter } from '../ui/fileHighlighter';
import { ContextManager } from '../context/contextManager';
-import { showDiffViewer } from '../ui/diffViewer';
-import { envConfig } from '../utils/envConfig';
-import path from 'path';
+import { RefactorManager } from '../ui/refactorManager';
+import { setTimeout } from 'timers/promises';
async function refactorLine(
smell: Smell,
@@ -16,9 +17,6 @@ async function refactorLine(
contextManager: ContextManager
) {
try {
- vscode.window.showInformationMessage(
- `Eco: Smell ID ${smell.messageId} on line ${smell.occurences[0].line}`
- );
const refactorResult = await refactorSmell(filePath, smell);
return refactorResult;
} catch (error) {
@@ -34,8 +32,19 @@ export async function refactorSelectedSmell(
) {
const { editor, filePath } = getEditorAndFilePath();
+ const pastData = contextManager.getWorkspaceData('refactorData');
+
+ // Clean up temp directory if not removed
+ if (pastData) {
+ if (fs.existsSync(pastData.tempDir)) {
+ fs.promises.rm(pastData.tempDir, { recursive: true });
+ }
+ }
+
if (!editor || !filePath) {
- vscode.window.showErrorMessage('Eco: Unable to proceed as no active editor or file path found.');
+ vscode.window.showErrorMessage(
+ 'Eco: Unable to proceed as no active editor or file path found.'
+ );
return;
}
@@ -46,7 +55,9 @@ export async function refactorSelectedSmell(
)[filePath].smells;
if (!smellsData || smellsData.length === 0) {
- vscode.window.showErrorMessage('Eco: No smells detected in the file for refactoring.');
+ vscode.window.showErrorMessage(
+ 'Eco: No smells detected in the file for refactoring.'
+ );
return;
}
@@ -55,7 +66,9 @@ export async function refactorSelectedSmell(
if (smellId) {
smellToRefactor = smellsData.find((smell: Smell) => smell.messageId === smellId);
} else {
- smellToRefactor = smellsData.find((smell: Smell) => selectedLine === smell.occurences[0].line);
+ smellToRefactor = smellsData.find(
+ (smell: Smell) => selectedLine === smell.occurences[0].line
+ );
}
if (!smellToRefactor) {
@@ -63,27 +76,41 @@ export async function refactorSelectedSmell(
return;
}
- // Refactor the smell
- const refactorResult = await refactorLine(smellToRefactor, filePath, contextManager);
+ const refactorResult = await vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Notification,
+ title: `Fetching refactoring for ${smellToRefactor.symbol} on line ${smellToRefactor.occurences[0].line}`
+ },
+ async (progress, token) => {
+ const result = await refactorLine(smellToRefactor, filePath, contextManager);
+
+ vscode.window.showInformationMessage(
+ 'Refactoring report available in sidebar.'
+ );
+
+ return result;
+ }
+ );
if (!refactorResult || !refactorResult.refactoredData) {
- vscode.window.showErrorMessage('Eco: Refactoring failed. See console for details.');
+ vscode.window.showErrorMessage(
+ 'Eco: Refactoring failed. See console for details.'
+ );
return;
}
const { refactoredData } = refactorResult;
- //Read the refactored code
- const refactoredCode = await fs.promises.readFile(refactoredData.targetFile, 'utf8');
+ startRefactoringSession(contextManager, editor, refactoredData);
- //Get the original code from the editor
- const originalCode = editor.document.getText();
-
- //Show the diff viewer
- await showDiffViewer(editor, refactoredCode, originalCode);
-
- // Clean up temporary files
- await fs.promises.rm(refactoredData.tempDir, { recursive: true, force: true });
+ if (refactorResult.updatedSmells.length) {
+ const fileHighlighter = new FileHighlighter(contextManager);
+ fileHighlighter.highlightSmells(editor, refactorResult.updatedSmells);
+ } else {
+ vscode.window.showWarningMessage(
+ 'Eco: No updated smells detected after refactoring.'
+ );
+ }
}
export async function refactorAllSmellsOfType(
@@ -92,6 +119,15 @@ export async function refactorAllSmellsOfType(
) {
const { editor, filePath } = getEditorAndFilePath();
+ const pastData = contextManager.getWorkspaceData('refactorData');
+
+ // Clean up temp directory if not removed
+ if (pastData) {
+ if (fs.existsSync(pastData.tempDir)) {
+ fs.promises.rm(pastData.tempDir, { recursive: true });
+ }
+ }
+
if (!editor) {
vscode.window.showErrorMessage(
'Eco: Unable to proceed as no active editor found.'
@@ -148,23 +184,32 @@ export async function refactorAllSmellsOfType(
combinedRefactoredData += '\n\n';
}
- fs.readFile(refactorResult.refactoredData.targetFile, (err, data) => {
- if (!err) {
- combinedRefactoredData += data.toString('utf8');
+ fs.readFile(
+ refactorResult.refactoredData.targetFile.refactored,
+ (err, data) => {
+ if (!err) {
+ combinedRefactoredData += data.toString('utf8');
+ }
}
- });
+ );
totalEnergySaved += refactorResult.refactoredData.energySaved;
if (refactorResult.updatedSmells) {
- allUpdatedSmells = [
- ...allUpdatedSmells,
- ...refactorResult.updatedSmells
- ];
+ allUpdatedSmells = [...allUpdatedSmells, ...refactorResult.updatedSmells];
}
}
}
+ /*
+ Once all refactorings are merge, need to write to a file so that it has a path
+ Also need to reconstruct the `RefactoredData` object by combining all `affectedFiles`
+ target file should be the same. Once implemented, just uncomment line below and pass in
+ the refactoredData.
+ */
+
+ // startRefactoringSession(contextManager,editor,combinedRefactoredData);
+
if (combinedRefactoredData) {
await RefactorManager.previewRefactor(editor, combinedRefactoredData);
vscode.window.showInformationMessage(
@@ -188,3 +233,50 @@ export async function refactorAllSmellsOfType(
);
}
}
+
+async function startRefactoringSession(
+ contextManager: ContextManager,
+ editor: vscode.TextEditor,
+ refactoredData: RefactoredData
+) {
+ // Store only the diff editor state
+ await contextManager.setWorkspaceData('refactorData', refactoredData);
+
+ await vscode.commands.executeCommand('extension.refactorSidebar.focus');
+
+ //Read the refactored code
+ const refactoredCode = vscode.Uri.file(refactoredData.targetFile.refactored);
+
+ //Get the original code from the editor
+ const originalCode = editor.document.uri;
+
+ const allFiles: ChangedFile[] = [
+ refactoredData.targetFile,
+ ...refactoredData.affectedFiles
+ ].map((file) => {
+ return {
+ original: vscode.Uri.file(file.original).toString(),
+ refactored: vscode.Uri.file(file.refactored).toString()
+ };
+ });
+
+ await contextManager.setWorkspaceData('activeDiff', {
+ files: allFiles,
+ firstOpen: true,
+ isOpen: true
+ });
+
+ await setTimeout(500);
+
+ const doc = await vscode.workspace.openTextDocument(originalCode);
+ await vscode.window.showTextDocument(doc, { preview: false });
+
+ //Show the diff viewer
+ vscode.commands.executeCommand(
+ 'vscode.diff',
+ originalCode,
+ refactoredCode,
+ 'Refactoring Comparison'
+ );
+ vscode.commands.executeCommand('ecooptimizer-vs-code-plugin.showRefactorSidebar');
+}
diff --git a/src/context/contextManager.ts b/src/context/contextManager.ts
index 58819a6..90b272d 100644
--- a/src/context/contextManager.ts
+++ b/src/context/contextManager.ts
@@ -8,11 +8,11 @@ export class ContextManager {
}
// Global state example
- public getGlobalData(
+ public getGlobalData(
key: string,
defaultVal: any = undefined
): any | undefined {
- return this.context.globalState.get(key, defaultVal);
+ return this.context.globalState.get(key, defaultVal);
}
public setGlobalData(key: string, value: any): Thenable {
@@ -20,11 +20,11 @@ export class ContextManager {
}
// Workspace state example
- public getWorkspaceData(
+ public getWorkspaceData(
key: string,
defaultVal: any = undefined
): any | undefined {
- return this.context.workspaceState.get(key, defaultVal);
+ return this.context.workspaceState.get(key, defaultVal);
}
public setWorkspaceData(key: string, value: any): Thenable {
diff --git a/src/extension.ts b/src/extension.ts
index 625e93f..6adc8de 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -8,6 +8,8 @@ import { LineSelectionManager } from './ui/lineSelectionManager';
import { ContextManager } from './context/contextManager';
import { wipeWorkCache } from './commands/wipeWorkCache';
import { updateHash } from './utils/hashDocs';
+import { RefactorSidebarProvider } from './ui/refactorView';
+import { handleEditorChanges } from './utils/handleEditorChange';
export function activate(context: vscode.ExtensionContext) {
console.log('Refactor Plugin activated');
@@ -30,11 +32,11 @@ export function activate(context: vscode.ExtensionContext) {
contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, fileHashes);
}
- console.log(
- `Smell detection map: ${contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!
- )}`
- );
+ // console.log(
+ // `Smell detection map: ${contextManager.getWorkspaceData(
+ // envConfig.SMELL_MAP_KEY!
+ // )}`
+ // );
// ===============================================================
// REGISTER COMMANDS
@@ -74,15 +76,53 @@ export function activate(context: vscode.ExtensionContext) {
);
context.subscriptions.push(wipeWorkCacheCmd);
+ // ===============================================================
+ // REGISTER VIEWS
+ // ===============================================================
+
+ // Register the webview provider for the refactoring webview
+ const refactorProvider = new RefactorSidebarProvider(context);
+ context.subscriptions.push(
+ vscode.window.registerWebviewViewProvider(
+ RefactorSidebarProvider.viewType,
+ refactorProvider
+ )
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.showRefactorSidebar',
+ () => refactorProvider.updateView()
+ )
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.pauseRefactorSidebar',
+ () => refactorProvider.pauseView()
+ )
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.clearRefactorSidebar',
+ () => refactorProvider.clearView()
+ )
+ );
+
// ===============================================================
// ADD LISTENERS
// ===============================================================
+ vscode.window.onDidChangeVisibleTextEditors(async (editors) => {
+ handleEditorChanges(contextManager, editors);
+ });
+
// Adds comments to lines describing the smell
const lineSelectManager = new LineSelectionManager(contextManager);
context.subscriptions.push(
vscode.window.onDidChangeTextEditorSelection((event) => {
- console.log(`Detected event: ${event.kind?.toString()}`);
+ console.log(`Detected line selection event`);
lineSelectManager.commentLine(event.textEditor);
})
diff --git a/src/global.d.ts b/src/global.d.ts
index 2d13903..07104d3 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -34,11 +34,16 @@ declare global {
additionalInfo: AdditionalInfo;
}
+ interface ChangedFile {
+ original: string;
+ refactored: string;
+ }
+
interface RefactoredData {
tempDir: string;
- targetFile: string;
+ targetFile: ChangedFile;
energySaved: number;
- refactoredFiles: string[];
+ affectedFiles: ChangedFile[];
}
interface RefactorOutput {
diff --git a/src/types.ts b/src/types.ts
index 09e25d6..472cab9 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,3 +1,5 @@
+import * as vscode from 'vscode';
+
export interface Occurrence {
line: number;
endLine?: number;
@@ -27,14 +29,25 @@ export interface Smell {
additionalInfo: AdditionalInfo;
}
+export interface ChangedFile {
+ original: string;
+ refactored: string;
+}
+
export interface RefactoredData {
tempDir: string;
- targetFile: string;
+ targetFile: ChangedFile;
energySaved: number;
- refactoredFiles: string[];
+ affectedFiles: ChangedFile[];
}
export interface RefactorOutput {
refactoredData?: RefactoredData; // Refactored code as a string
updatedSmells: Smell[]; //
}
+
+export interface ActiveDiff {
+ files: ChangedFile[];
+ isOpen: boolean;
+ firstOpen: boolean;
+}
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
new file mode 100644
index 0000000..6e4073c
--- /dev/null
+++ b/src/ui/refactorView.ts
@@ -0,0 +1,155 @@
+import * as vscode from 'vscode';
+import path from 'path';
+import { readFileSync } from 'fs';
+import { ActiveDiff } from '../types';
+import { promises as fs } from 'fs';
+
+export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
+ public static readonly viewType = 'extension.refactorSidebar';
+ private _view?: vscode.WebviewView;
+ private _file_map: Map = new Map();
+
+ constructor(private readonly _context: vscode.ExtensionContext) {}
+
+ resolveWebviewView(
+ webviewView: vscode.WebviewView,
+ context: vscode.WebviewViewResolveContext,
+ _token: vscode.CancellationToken
+ ) {
+ this._view = webviewView;
+ const webview = webviewView.webview;
+
+ webview.options = {
+ enableScripts: true
+ };
+
+ webview.html = this._getHtml(webview);
+
+ webviewView.onDidChangeVisibility(async () => {
+ console.log('Webview is visible');
+ if (webviewView.visible) {
+ // Use acquireVsCodeApi to get the webview state
+ const savedState =
+ this._context.workspaceState.get('refactorData');
+
+ if (savedState) {
+ this.updateView();
+ return;
+ }
+ }
+ });
+
+ webviewView.onDidDispose(() => {
+ console.log('Webview Disposed');
+ });
+
+ webviewView.webview.onDidReceiveMessage(async (message) => {
+ switch (message.command) {
+ case 'selectFile':
+ vscode.commands.executeCommand(
+ 'vscode.diff',
+ vscode.Uri.file(message.original),
+ vscode.Uri.file(message.refactored),
+ 'Refactoring Comparison'
+ );
+ break;
+ case 'accept':
+ await this.applyRefactoring();
+ this.closeViews();
+ break;
+ case 'reject':
+ this.closeViews();
+ break;
+ }
+ });
+ console.log('Initialized sidebar view');
+ }
+
+ async updateView() {
+ console.log('Updating view');
+ const refactoredData =
+ this._context.workspaceState.get('refactorData')!;
+
+ this._file_map.set(
+ vscode.Uri.file(refactoredData.targetFile.original),
+ vscode.Uri.file(refactoredData.targetFile.refactored)
+ );
+
+ refactoredData.affectedFiles.forEach(({ original, refactored }) => {
+ this._file_map!.set(vscode.Uri.file(original), vscode.Uri.file(refactored));
+ });
+
+ if (this._view) {
+ this.openView(refactoredData);
+ }
+ }
+
+ private async openView(refactoredData: RefactoredData) {
+ const diffView = this._context.workspaceState.get('activeDiff')!;
+
+ if (diffView.isOpen) {
+ console.log('starting view');
+ this._view!.show(true);
+ this._view!.webview.postMessage({ command: 'update', data: refactoredData });
+ } else {
+ console.log('Gonna pause');
+ this.pauseView();
+ }
+ }
+
+ pauseView() {
+ console.log('pausing view');
+ this._view!.webview.postMessage({ command: 'pause' });
+ }
+
+ clearView() {
+ this._view?.webview.postMessage({ command: 'clear' });
+ this._file_map = new Map();
+ }
+
+ private _getHtml(webview: vscode.Webview): string {
+ const scriptUri = webview.asWebviewUri(
+ vscode.Uri.file(path.join(this._context.extensionPath, 'media', 'script.js'))
+ );
+ const customCssUri = webview.asWebviewUri(
+ vscode.Uri.file(path.join(this._context.extensionPath, 'media', 'style.css'))
+ );
+ const vscodeCssUri = webview.asWebviewUri(
+ vscode.Uri.file(path.join(this._context.extensionPath, 'media', 'vscode.css'))
+ );
+ const htmlPath = path.join(this._context.extensionPath, 'media', 'webview.html');
+ let htmlContent = readFileSync(htmlPath, 'utf8');
+
+ // Inject the script URI dynamically
+ htmlContent = htmlContent.replace('${vscodeCssUri}', vscodeCssUri.toString());
+ htmlContent = htmlContent.replace('${customCssUri}', customCssUri.toString());
+ htmlContent = htmlContent.replace('${scriptUri}', scriptUri.toString());
+
+ return htmlContent;
+ }
+
+ private closeViews() {
+ this._file_map = new Map();
+ vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+ vscode.commands.executeCommand('workbench.view.explorer');
+
+ const tempDir =
+ this._context.workspaceState.get('refactorData')?.tempDir!;
+
+ fs.rm(tempDir, { recursive: true });
+
+ this._context.workspaceState.update('activeDiff', undefined);
+ this._context.workspaceState.update('refactorData', undefined);
+ }
+
+ private async applyRefactoring() {
+ this._file_map!.forEach(async (refactored, original) => {
+ vscode.window.showInformationMessage('Applying Eco changes...');
+ console.log(`refactored: ${refactored}\noriginal: ${original}`);
+ const modifiedContent = await vscode.workspace.fs.readFile(refactored);
+
+ await vscode.workspace.fs.writeFile(original, modifiedContent);
+ });
+ vscode.window.showInformationMessage('Refactoring applied successfully!');
+ }
+}
diff --git a/src/utils/handleEditorChange.ts b/src/utils/handleEditorChange.ts
new file mode 100644
index 0000000..8756871
--- /dev/null
+++ b/src/utils/handleEditorChange.ts
@@ -0,0 +1,118 @@
+import * as vscode from 'vscode';
+import { setTimeout } from 'timers/promises';
+
+import { ContextManager } from '../context/contextManager';
+import { ActiveDiff } from '../types';
+
+interface DiffInfo {
+ original: vscode.Uri;
+ modified: vscode.Uri;
+}
+
+export async function handleEditorChanges(
+ contextManager: ContextManager,
+ editors: readonly vscode.TextEditor[]
+) {
+ console.log('Detected visible editor change');
+ const diffState = contextManager.getWorkspaceData('activeDiff');
+ const refactorData =
+ contextManager.getWorkspaceData('refactorData');
+
+ if (!diffState) {
+ console.log('No active refactoring session');
+ return;
+ }
+
+ // console.log(`diffstate: ${diffState.isOpen}`);
+ // console.log(`diffstate: ${JSON.stringify(diffState)}`);
+ // console.log(`Editors: ${JSON.stringify(editors)}`);
+
+ // Is a diff editor for a refactoring
+ const isDiffRefactorEditor = isDiffEditorOpen(editors, diffState);
+
+ if (diffState.isOpen) {
+ // User either closed or switched diff editor
+ // console.log(`refactor data: ${JSON.stringify(refactorData)}`);
+ // console.log(`is diff editor: ${isDiffRefactorEditor}`);
+
+ if (isDiffRefactorEditor === undefined) {
+ return;
+ }
+ if ((!isDiffRefactorEditor || !refactorData) && !diffState.firstOpen) {
+ console.log('Diff editor no longer active');
+ diffState.isOpen = false;
+ // console.log(`diffstate: ${diffState.isOpen}`);
+ // console.log(`diffstate: ${JSON.stringify(diffState)}`);
+ contextManager.setWorkspaceData('activeDiff', diffState);
+ await setTimeout(500);
+ vscode.commands.executeCommand(
+ 'ecooptimizer-vs-code-plugin.pauseRefactorSidebar'
+ );
+ return;
+ }
+ if (diffState.firstOpen) {
+ diffState.firstOpen = false;
+ contextManager.setWorkspaceData('activeDiff', diffState);
+ await setTimeout(500);
+ }
+ // switched from one diff editor to another, no handling needed
+ console.log('continuing');
+ return;
+ }
+
+ // Diff editor was reopened (switch back to)
+ else if (isDiffRefactorEditor) {
+ console.log('Opening Sidebar');
+ // console.log(`diffstate: ${diffState.isOpen}`);
+ diffState.isOpen = true;
+ // console.log(`diffstate: ${JSON.stringify(diffState)}`);
+ contextManager.setWorkspaceData('activeDiff', diffState);
+ await setTimeout(500);
+ vscode.commands.executeCommand(
+ 'ecooptimizer-vs-code-plugin.showRefactorSidebar'
+ );
+ }
+ console.log('Doing nothing');
+}
+
+function isDiffEditorOpen(
+ editors: readonly vscode.TextEditor[],
+ diffState: ActiveDiff
+) {
+ console.log('Checking if editor is a diff editor');
+ if (!editors.length) {
+ console.log('No editors found');
+ return undefined;
+ }
+
+ // @ts-ignore
+ const diffInfo: DiffInfo[] = editors[0].diffInformation;
+ // console.log(`Diff Info: ${JSON.stringify(diffInfo)}`);
+
+ if (!diffInfo && editors.length === 2) {
+ console.log('Checking first case');
+
+ return diffState.files.some((file) => {
+ // console.log(`file: ${JSON.stringify(file)}`);
+ return (
+ (file.original === editors[0].document.uri.toString() &&
+ file.refactored === editors[1].document.uri.toString()) ||
+ (file.refactored === editors[0].document.uri.toString() &&
+ file.original === editors[1].document.uri.toString())
+ );
+ });
+ } else if (diffInfo && diffInfo.length === 1) {
+ console.log('Checking second case');
+ return diffState.files.some((file) => {
+ // console.log(`file: ${JSON.stringify(file)}`);
+ return (
+ (file.original === diffInfo[0].original.toString() &&
+ file.refactored === diffInfo[0].modified.toString()) ||
+ (file.original === diffInfo[0].modified.toString() &&
+ file.refactored === diffInfo[0].original.toString())
+ );
+ });
+ }
+
+ return false;
+}
diff --git a/src/utils/hashDocs.ts b/src/utils/hashDocs.ts
index 50f7114..a4a982d 100644
--- a/src/utils/hashDocs.ts
+++ b/src/utils/hashDocs.ts
@@ -13,6 +13,7 @@ export async function updateHash(
contextManager: ContextManager,
document: vscode.TextDocument
) {
+ console.log(`Updating hash for ${document.fileName}`);
const lastSavedHashes = contextManager.getWorkspaceData(
envConfig.FILE_CHANGES_KEY!,
{}
diff --git a/tsconfig.json b/tsconfig.json
index 8ce50e7..1da27ad 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,12 +3,15 @@
"module": "Node16",
"target": "ES2022",
"lib": [
- "ES2022"
+ "ES2022",
+ "DOM",
+ "DOM.Iterable"
],
"sourceMap": true,
"rootDir": "src",
"strict": true, /* enable all strict type-checking options */
- "typeRoots": ["./node_modules/@types", "./types"]
+ "typeRoots": ["./node_modules/@types", "./types"],
+ "forceConsistentCasingInFileNames": true
},
- "include": ["src/global.d.ts", "src/**/*"]
+ "include": ["src/global.d.ts", "src/**/*", "media/script.js"]
}
From 5cdba76e6aed3315f9144b9fe93516aca8b2be29 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 3 Feb 2025 11:54:15 -0500
Subject: [PATCH 23/83] fixed issue where sidebar reloads on file navigation
(ssm-lab/capstone--sco-vs-code-plugin#353)
---
media/script.js | 12 ++++++------
src/ui/refactorView.ts | 6 +++++-
src/utils/handleEditorChange.ts | 7 +++++++
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/media/script.js b/media/script.js
index 14cd794..db08d53 100644
--- a/media/script.js
+++ b/media/script.js
@@ -62,12 +62,12 @@ function clearWebview() {
}
// Restore state when webview loads
-// window.addEventListener('DOMContentLoaded', () => {
-// const savedState = vscode.getState();
-// if (savedState) {
-// updateWebView(savedState);
-// }
-// });
+window.addEventListener('DOMContentLoaded', () => {
+ const savedState = vscode.getState();
+ if (savedState) {
+ updateWebView(savedState);
+ }
+});
// Listen for extension messages
window.addEventListener('message', (event) => {
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
index 6e4073c..582e6d7 100644
--- a/src/ui/refactorView.ts
+++ b/src/ui/refactorView.ts
@@ -3,6 +3,7 @@ import path from 'path';
import { readFileSync } from 'fs';
import { ActiveDiff } from '../types';
import { promises as fs } from 'fs';
+import { sidebarState } from '../utils/handleEditorChange';
export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
public static readonly viewType = 'extension.refactorSidebar';
@@ -46,12 +47,15 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
webviewView.webview.onDidReceiveMessage(async (message) => {
switch (message.command) {
case 'selectFile':
- vscode.commands.executeCommand(
+ sidebarState.isOpening = true;
+ console.log('Switching diff file view.');
+ await vscode.commands.executeCommand(
'vscode.diff',
vscode.Uri.file(message.original),
vscode.Uri.file(message.refactored),
'Refactoring Comparison'
);
+ sidebarState.isOpening = false;
break;
case 'accept':
await this.applyRefactoring();
diff --git a/src/utils/handleEditorChange.ts b/src/utils/handleEditorChange.ts
index 8756871..db86c9e 100644
--- a/src/utils/handleEditorChange.ts
+++ b/src/utils/handleEditorChange.ts
@@ -9,6 +9,8 @@ interface DiffInfo {
modified: vscode.Uri;
}
+export let sidebarState = { isOpening: false };
+
export async function handleEditorChanges(
contextManager: ContextManager,
editors: readonly vscode.TextEditor[]
@@ -18,6 +20,10 @@ export async function handleEditorChanges(
const refactorData =
contextManager.getWorkspaceData('refactorData');
+ if (sidebarState.isOpening) {
+ return;
+ }
+
if (!diffState) {
console.log('No active refactoring session');
return;
@@ -38,6 +44,7 @@ export async function handleEditorChanges(
if (isDiffRefactorEditor === undefined) {
return;
}
+
if ((!isDiffRefactorEditor || !refactorData) && !diffState.firstOpen) {
console.log('Diff editor no longer active');
diffState.isOpen = false;
From 8304af52ee200c5275d39637593cddee856643f2 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 3 Feb 2025 14:26:26 -0500
Subject: [PATCH 24/83] Updated refactor functionality to work with refactoring
all smells
---
media/script.js | 45 +++++++++++++++++++++++-----
src/commands/refactorSmell.ts | 56 +++++++++++++++++++++++++++--------
src/context/contextManager.ts | 8 ++---
src/ui/refactorView.ts | 21 ++++++++++---
4 files changed, 102 insertions(+), 28 deletions(-)
diff --git a/media/script.js b/media/script.js
index db08d53..f1a782f 100644
--- a/media/script.js
+++ b/media/script.js
@@ -1,6 +1,6 @@
const vscode = acquireVsCodeApi();
-function updateWebView(data) {
+function updateWebView(data, sep) {
// Hide "No refactoring in progress" message
document.getElementById('no-data').style.display = 'none';
document.getElementById('container').style.display = 'block';
@@ -8,20 +8,26 @@ function updateWebView(data) {
// Update Energy Saved
document.getElementById(
'energy'
- ).textContent = `Energy Saved: ${data.energySaved.toExponential(3)} J`;
+ ).textContent = `Energy Saved: ${data.energySaved.toExponential(3)} kg CO2`;
// Populate Target File
+ const targetFile = data.targetFile;
const targetFileList = document.getElementById('target-file-list');
targetFileList.innerHTML = '';
- const targetFile = data.targetFile.refactored.replace(data.tempDir, '');
const li = document.createElement('li');
- li.textContent = targetFile;
+
+ const relFile = findRelPath(targetFile.refactored, sep);
+ if (relFile.length === 0) {
+ relFile = targetFile.original;
+ }
+ li.textContent = relFile;
+
li.classList.add('clickable');
li.onclick = () => {
vscode.postMessage({
command: 'selectFile',
- original: data.targetFile.original,
- refactored: data.targetFile.refactored
+ original: targetFile.original,
+ refactored: targetFile.refactored
});
};
targetFileList.appendChild(li);
@@ -34,7 +40,13 @@ function updateWebView(data) {
}
data.affectedFiles.forEach((file) => {
const li = document.createElement('li');
- li.textContent = file.refactored.replace(data.tempDir, '');
+ const relFile = findRelPath(file.refactored, sep);
+
+ if (relFile.length === 0) {
+ relFile = file.original;
+ }
+
+ li.textContent = relFile;
li.classList.add('clickable');
li.onclick = () => {
vscode.postMessage({
@@ -72,7 +84,7 @@ window.addEventListener('DOMContentLoaded', () => {
// Listen for extension messages
window.addEventListener('message', (event) => {
if (event.data.command === 'update') {
- updateWebView(event.data.data);
+ updateWebView(event.data.data, event.data.sep);
} else if (event.data.command === 'clear') {
clearWebview();
} else if (event.data.command === 'pause') {
@@ -91,3 +103,20 @@ document.getElementById('reject-btn').addEventListener('click', () => {
vscode.postMessage({ command: 'reject' });
clearWebview();
});
+
+function findRelPath(filePath, sep) {
+ // Split the path using the separator
+ const parts = filePath.split(sep);
+
+ // Find the index of the part containing the 'ecooptimizer-' substring
+ const index = parts.findIndex((part) => part.includes('ecooptimizer-'));
+
+ // If a matching part is found, return the joined list of items after it
+ if (index !== -1) {
+ // Slice the array from the next index and join them with the separator
+ return parts.slice(index + 1).join(sep);
+ }
+
+ // Return an empty string if no match is found
+ return '';
+}
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 0d0c761..1a7c067 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -10,6 +10,14 @@ import { FileHighlighter } from '../ui/fileHighlighter';
import { ContextManager } from '../context/contextManager';
import { RefactorManager } from '../ui/refactorManager';
import { setTimeout } from 'timers/promises';
+import { inherits } from 'util';
+
+export interface MultiRefactoredData {
+ tempDirs: string[];
+ targetFile: ChangedFile;
+ affectedFiles: ChangedFile[];
+ energySaved: number;
+}
async function refactorLine(
smell: Smell,
@@ -32,13 +40,11 @@ export async function refactorSelectedSmell(
) {
const { editor, filePath } = getEditorAndFilePath();
- const pastData = contextManager.getWorkspaceData('refactorData');
+ const pastData = contextManager.getWorkspaceData('refactorData');
// Clean up temp directory if not removed
if (pastData) {
- if (fs.existsSync(pastData.tempDir)) {
- fs.promises.rm(pastData.tempDir, { recursive: true });
- }
+ cleanTemps(pastData);
}
if (!editor || !filePath) {
@@ -123,9 +129,7 @@ export async function refactorAllSmellsOfType(
// Clean up temp directory if not removed
if (pastData) {
- if (fs.existsSync(pastData.tempDir)) {
- fs.promises.rm(pastData.tempDir, { recursive: true });
- }
+ cleanTemps(pastData);
}
if (!editor) {
@@ -202,12 +206,27 @@ export async function refactorAllSmellsOfType(
}
/*
- Once all refactorings are merge, need to write to a file so that it has a path
- Also need to reconstruct the `RefactoredData` object by combining all `affectedFiles`
- target file should be the same. Once implemented, just uncomment line below and pass in
- the refactoredData.
+ Once all refactorings are merge, need to write to a file so that it has a path that
+ will be the new `targetFile`. Also need to reconstruct the `RefactoredData` object
+ by combining all `affectedFiles` merge to new paths if applicable. Once implemented,
+ just uncomment lines below and pass in the refactoredData.
*/
+ // Tentative data structure to be built below, change inputs as needed but needs
+ // to implement the `MultiRefactoredData` interface
+
+ // For any temp files that need to be written due to merging, I'd suggest writing them all
+ // to one temp directory and add that directory to allTempDirs, that way they will be removed
+
+ // UNCOMMENT ME WHEN READY
+ // const combinedRefactoredData: MultiRefactoredData = {
+ // targetFile: combinedTargetFile,
+ // affectedFiles: allAffectedFiles,
+ // energySaved: totalEnergySaved,
+ // tempDirs: allTempDirs
+ // }
+
+ // UNCOMMENT ME WHEN READY
// startRefactoringSession(contextManager,editor,combinedRefactoredData);
if (combinedRefactoredData) {
@@ -237,7 +256,7 @@ export async function refactorAllSmellsOfType(
async function startRefactoringSession(
contextManager: ContextManager,
editor: vscode.TextEditor,
- refactoredData: RefactoredData
+ refactoredData: RefactoredData | MultiRefactoredData
) {
// Store only the diff editor state
await contextManager.setWorkspaceData('refactorData', refactoredData);
@@ -280,3 +299,16 @@ async function startRefactoringSession(
);
vscode.commands.executeCommand('ecooptimizer-vs-code-plugin.showRefactorSidebar');
}
+
+function cleanTemps(pastData: any) {
+ console.log('Cleaning up stale artifacts');
+ const tempDirs = pastData!.tempDir! || pastData!.tempDirs!;
+
+ if (Array.isArray(tempDirs)) {
+ tempDirs.forEach((dir) => {
+ fs.promises.rm(dir, { recursive: true });
+ });
+ } else {
+ fs.promises.rm(tempDirs!, { recursive: true });
+ }
+}
diff --git a/src/context/contextManager.ts b/src/context/contextManager.ts
index 90b272d..ed67f95 100644
--- a/src/context/contextManager.ts
+++ b/src/context/contextManager.ts
@@ -8,10 +8,10 @@ export class ContextManager {
}
// Global state example
- public getGlobalData(
+ public getGlobalData(
key: string,
defaultVal: any = undefined
- ): any | undefined {
+ ): T | undefined {
return this.context.globalState.get(key, defaultVal);
}
@@ -20,10 +20,10 @@ export class ContextManager {
}
// Workspace state example
- public getWorkspaceData(
+ public getWorkspaceData(
key: string,
defaultVal: any = undefined
- ): any | undefined {
+ ): T | undefined {
return this.context.workspaceState.get(key, defaultVal);
}
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
index 582e6d7..701f70c 100644
--- a/src/ui/refactorView.ts
+++ b/src/ui/refactorView.ts
@@ -4,6 +4,7 @@ import { readFileSync } from 'fs';
import { ActiveDiff } from '../types';
import { promises as fs } from 'fs';
import { sidebarState } from '../utils/handleEditorChange';
+import { MultiRefactoredData } from '../commands/refactorSmell';
export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
public static readonly viewType = 'extension.refactorSidebar';
@@ -94,7 +95,11 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
if (diffView.isOpen) {
console.log('starting view');
this._view!.show(true);
- this._view!.webview.postMessage({ command: 'update', data: refactoredData });
+ this._view!.webview.postMessage({
+ command: 'update',
+ data: refactoredData,
+ sep: path.sep
+ });
} else {
console.log('Gonna pause');
this.pauseView();
@@ -137,10 +142,18 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
vscode.commands.executeCommand('workbench.action.closeActiveEditor');
vscode.commands.executeCommand('workbench.view.explorer');
- const tempDir =
- this._context.workspaceState.get('refactorData')?.tempDir!;
+ const tempDirs =
+ this._context.workspaceState.get('refactorData')?.tempDir! ||
+ this._context.workspaceState.get('refactorData')
+ ?.tempDirs;
- fs.rm(tempDir, { recursive: true });
+ if (Array.isArray(tempDirs)) {
+ tempDirs.forEach((dir) => {
+ fs.rm(dir, { recursive: true });
+ });
+ } else {
+ fs.rm(tempDirs!, { recursive: true });
+ }
this._context.workspaceState.update('activeDiff', undefined);
this._context.workspaceState.update('refactorData', undefined);
From 123e04fc212f09d4c224e16cb4575944cc7b2b15 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 3 Feb 2025 18:34:22 -0500
Subject: [PATCH 25/83] fixed bug where refactorings were deleted before they
could be applied
---
src/ui/refactorView.ts | 29 ++++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
index 701f70c..6526332 100644
--- a/src/ui/refactorView.ts
+++ b/src/ui/refactorView.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
import path from 'path';
import { readFileSync } from 'fs';
import { ActiveDiff } from '../types';
-import { promises as fs } from 'fs';
+import * as fs from 'fs';
import { sidebarState } from '../utils/handleEditorChange';
import { MultiRefactoredData } from '../commands/refactorSmell';
@@ -59,11 +59,11 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
sidebarState.isOpening = false;
break;
case 'accept':
- await this.applyRefactoring();
- this.closeViews();
+ this.applyRefactoring();
+ await this.closeViews();
break;
case 'reject':
- this.closeViews();
+ await this.closeViews();
break;
}
});
@@ -137,8 +137,9 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
return htmlContent;
}
- private closeViews() {
- this._file_map = new Map();
+ private async closeViews() {
+ console.log('Cleaning up webview');
+ this.clearView();
vscode.commands.executeCommand('workbench.action.closeActiveEditor');
vscode.commands.executeCommand('workbench.view.explorer');
@@ -148,24 +149,26 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
?.tempDirs;
if (Array.isArray(tempDirs)) {
- tempDirs.forEach((dir) => {
- fs.rm(dir, { recursive: true });
+ tempDirs.forEach(async (dir) => {
+ await fs.promises.rm(dir, { recursive: true });
});
} else {
- fs.rm(tempDirs!, { recursive: true });
+ await fs.promises.rm(tempDirs!, { recursive: true });
}
this._context.workspaceState.update('activeDiff', undefined);
this._context.workspaceState.update('refactorData', undefined);
}
- private async applyRefactoring() {
- this._file_map!.forEach(async (refactored, original) => {
+ private applyRefactoring() {
+ this._file_map!.forEach((refactored, original) => {
vscode.window.showInformationMessage('Applying Eco changes...');
console.log(`refactored: ${refactored}\noriginal: ${original}`);
- const modifiedContent = await vscode.workspace.fs.readFile(refactored);
+ const modifiedContent = fs.readFileSync(refactored.fsPath, {
+ encoding: 'utf-8'
+ });
- await vscode.workspace.fs.writeFile(original, modifiedContent);
+ fs.writeFileSync(original.fsPath, modifiedContent);
});
vscode.window.showInformationMessage('Refactoring applied successfully!');
}
From 18226f0d84127b09cfbf6a0f8d9cd059149cfa2c Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 3 Feb 2025 21:52:03 -0500
Subject: [PATCH 26/83] Added functionality to highlight smells in different
colours based on type
closes ssm-lab/capstone--source-code-optimizer#351
---
src/types.ts | 6 +++
src/ui/fileHighlighter.ts | 98 +++++++++++++++++++++------------------
src/utils/smellDetails.ts | 85 +++++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+), 44 deletions(-)
create mode 100644 src/utils/smellDetails.ts
diff --git a/src/types.ts b/src/types.ts
index 472cab9..b6e5e52 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -51,3 +51,9 @@ export interface ActiveDiff {
isOpen: boolean;
firstOpen: boolean;
}
+
+export type SmellDetails = {
+ symbol: string;
+ message: string;
+ colour: string; // RGB colour as a string
+};
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index aa603b3..a3259c3 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -2,66 +2,49 @@ import * as vscode from 'vscode';
import { getEditor } from '../utils/editorUtils';
import { ContextManager } from '../context/contextManager';
import { HoverManager } from './hoverManager';
+import { SMELL_MAP } from '../utils/smellDetails';
export class FileHighlighter {
private contextManager;
- private decoration: vscode.TextEditorDecorationType | null = null;
+ private decorations: vscode.TextEditorDecorationType[] = [];
public constructor(contextManager: ContextManager) {
this.contextManager = contextManager;
}
public resetHighlights() {
- if (this.decoration) {
- console.log('Removing decoration');
- this.decoration.dispose();
+ if (this.decorations.length > 0) {
+ console.log('Removing decorations');
+ this.decorations.forEach((decoration) => {
+ decoration.dispose();
+ });
}
}
public highlightSmells(editor: vscode.TextEditor, smells: Smell[]) {
this.resetHighlights();
- const underline = vscode.window.createTextEditorDecorationType({
- textDecoration: 'wavy rgba(76, 245, 96, 0.62) underline 1px'
- });
+ const activeSmells = new Set(smells.map((smell) => smell.messageId));
- const flashlight = vscode.window.createTextEditorDecorationType({
- isWholeLine: true,
- backgroundColor: 'rgba(249, 209, 10, 0.3)'
- });
-
- const aLittleExtra = vscode.window.createTextEditorDecorationType({
- borderWidth: '1px 2px 1px 0', // Top, Right, Bottom, No Left border
- borderStyle: 'solid',
- borderColor: 'rgba(76, 245, 96, 0.62)', // Change as needed
- after: {
- contentText: 'βΆ', // Unicode right arrow
- margin: '0 0 0 5px', // Space between line and arrow
- color: 'rgba(76, 245, 96, 0.62)',
- fontWeight: 'bold'
- },
- overviewRulerColor: 'rgba(76, 245, 96, 0.62)',
- overviewRulerLane: vscode.OverviewRulerLane.Right
+ activeSmells.forEach((smellType) => {
+ this.highlightSmell(editor, smells, smellType);
});
- const padding = vscode.window.createTextEditorDecorationType({
- after: {
- contentText: ' '
- }
- });
+ console.log('Updated smell line highlights');
+ }
- const seenLines: number[] = [];
+ public highlightSmell(
+ editor: vscode.TextEditor,
+ smells: Smell[],
+ targetSmell: string
+ ) {
const smellLines: vscode.DecorationOptions[] = smells
.filter((smell: Smell) => {
const valid = smell.occurences.every((occurrence: { line: number }) =>
isValidLine(occurrence.line)
);
- if (valid && seenLines.includes(smell.occurences[0].line)) {
- return false;
- } else if (valid) {
- seenLines.push(smell.occurences[0].line);
- }
- return valid;
+ const isCorrectType = smell.messageId === targetSmell;
+ return valid && isCorrectType;
})
.map((smell: Smell) => {
const line = smell.occurences[0].line - 1; // convert to zero-based line index for VS editor
@@ -73,19 +56,46 @@ export class FileHighlighter {
const range = new vscode.Range(line, indexStart, line, indexEnd);
- const hoverManager = HoverManager.getInstance(
- this.contextManager,
- smells
- );
+ const hoverManager = HoverManager.getInstance(this.contextManager, smells);
return { range, hoverMessage: hoverManager.hoverContent || undefined }; // option to hover over and read smell details
});
- this.decoration = aLittleExtra;
+ const colorOfSmell = SMELL_MAP.get(targetSmell)!.colour;
- editor.setDecorations(padding, smellLines);
- editor.setDecorations(this.decoration, smellLines);
+ editor.setDecorations(this.getDecoration(colorOfSmell), smellLines);
+ }
- console.log('Updated smell line highlights');
+ private getDecoration(color: string) {
+ // ================= EXTRA DECORATIONS ===========================
+ const underline = vscode.window.createTextEditorDecorationType({
+ textDecoration: `wavy ${color} underline 1px`
+ });
+
+ const flashlight = vscode.window.createTextEditorDecorationType({
+ isWholeLine: true,
+ backgroundColor: color
+ });
+ // ================================================================
+
+ const aLittleExtra = vscode.window.createTextEditorDecorationType({
+ borderWidth: '1px 2px 1px 0', // Top, Right, Bottom, No Left border
+ borderStyle: 'solid',
+ borderColor: color, // Change as needed
+ after: {
+ contentText: 'βΆ', // Unicode right arrow
+ margin: '0 0 0 5px', // Space between line and arrow
+ color: color,
+ fontWeight: 'bold'
+ },
+ overviewRulerColor: color,
+ overviewRulerLane: vscode.OverviewRulerLane.Right
+ });
+
+ const decoration = aLittleExtra; // Select decoration
+
+ this.decorations.push(decoration);
+
+ return decoration;
}
}
diff --git a/src/utils/smellDetails.ts b/src/utils/smellDetails.ts
new file mode 100644
index 0000000..897a15a
--- /dev/null
+++ b/src/utils/smellDetails.ts
@@ -0,0 +1,85 @@
+import { SmellDetails } from '../types';
+
+export const SMELL_MAP: Map = new Map([
+ [
+ 'R1729', // id: "use-a-generator"
+ {
+ symbol: 'use-a-generator',
+ message:
+ 'Refactor to use a generator expression instead of a list comprehension inside `any()` or `all()`. This improves memory efficiency by avoiding the creation of an intermediate list.',
+ colour: 'rgb(255, 204, 0)' // Yellow
+ }
+ ],
+ [
+ 'R0913', // id: "too-many-arguments"
+ {
+ symbol: 'too-many-arguments',
+ message:
+ 'Refactor the function to reduce the number of parameters. Functions with too many arguments can become difficult to maintain and understand. Consider breaking it into smaller, more manageable functions.',
+ colour: 'rgb(255, 102, 102)' // Light Red
+ }
+ ],
+ [
+ 'R6301', // id: "no-self-use"
+ {
+ symbol: 'no-self-use',
+ message:
+ "Refactor the method to make it static, as it does not use `self`. Static methods do not require an instance and improve clarity and performance when the method doesn't depend on instance data.",
+ colour: 'rgb(204, 255, 255)' // Light Cyan
+ }
+ ],
+ [
+ 'LLE001', // id: "long-lambda-expression"
+ {
+ symbol: 'long-lambda-expression',
+ message:
+ 'Refactor the lambda expression to improve readability. Long lambda expressions can be confusing; breaking them into named functions can make the code more understandable and maintainable.',
+ colour: 'rgb(153, 102, 255)' // Light Purple
+ }
+ ],
+ [
+ 'LMC001', // id: "long-message-chain"
+ {
+ symbol: 'long-message-chain',
+ message:
+ 'Refactor the message chain to improve readability and performance. Long chains of method calls can be hard to follow and may impact performance. Consider breaking them into smaller steps.',
+ colour: 'rgb(255, 204, 255)' // Light Pink
+ }
+ ],
+ [
+ 'UVA001', // id: "unused_variables_and_attributes"
+ {
+ symbol: 'unused-variables-and-attributes',
+ message:
+ 'Remove unused variables or attributes to clean up the code. Keeping unused elements in the code increases its complexity without providing any benefit, making it harder to maintain.',
+ colour: 'rgb(255, 255, 102)' // Light Yellow
+ }
+ ],
+ [
+ 'LEC001', // id: "long-element-chain"
+ {
+ symbol: 'long-element-chain',
+ message:
+ 'Refactor the long element chain for better performance and clarity. Chains of nested elements are harder to read and can lead to inefficiency, especially when accessing deep levels repeatedly.',
+ colour: 'rgb(204, 204, 255)' // Light Blue
+ }
+ ],
+ [
+ 'CRC001', // id: "cached-repeated-calls"
+ {
+ symbol: 'cached-repeated-calls',
+ message:
+ 'Refactor by caching repeated function calls to improve performance. Repeated calls to the same function can be avoided by storing the result, which saves processing time and enhances performance.',
+ colour: 'rgb(102, 255, 102)' // Light Green
+ }
+ ],
+ [
+ 'SCL001', // id: "string-concat-loop"
+ {
+ symbol: 'string-concat-loop',
+ message:
+ 'Refactor to use list accumulation instead of string concatenation inside a loop. Concatenating strings in a loop is inefficient; list accumulation and joining are faster and use less memory.',
+ colour: 'rgb(255, 178, 102)' // Light Orange
+ }
+ ]
+]);
From 45430bd6e5a43df17d1ffb15e32cb6a950e801c5 Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Thu, 6 Feb 2025 13:01:01 -0500
Subject: [PATCH 27/83] Fixed up refactorSmell not refactoring the selected
smell line #345
---
src/commands/refactorSmell.ts | 6 +++---
src/ui/hoverManager.ts | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 1a7c067..b6d3bf0 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -36,7 +36,7 @@ async function refactorLine(
export async function refactorSelectedSmell(
contextManager: ContextManager,
- smellId?: string
+ smellGiven?: Smell
) {
const { editor, filePath } = getEditorAndFilePath();
@@ -69,8 +69,8 @@ export async function refactorSelectedSmell(
// Find the smell to refactor
let smellToRefactor: Smell | undefined;
- if (smellId) {
- smellToRefactor = smellsData.find((smell: Smell) => smell.messageId === smellId);
+ if (smellGiven?.messageId) {
+ smellToRefactor = smellsData.find((smell: Smell) => smell.messageId === smellGiven.messageId && smellGiven.occurences[0].line === smell.occurences[0].line);
} else {
smellToRefactor = smellsData.find(
(smell: Smell) => selectedLine === smell.occurences[0].line
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index edde3f4..11275fd 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -98,7 +98,7 @@ export class HoverManager {
'extension.refactorThisSmell',
async (smell: Smell) => {
const contextManager = new ContextManager(this.vscodeContext);
- await refactorSelectedSmell(contextManager, smell.messageId);
+ await refactorSelectedSmell(contextManager, smell);
}
),
// clicking "Refactor All Smells of this Type..."
From bc8a385e46cb455be1d1bb91dddfaeafb4ec2f57 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 6 Feb 2025 14:50:28 -0500
Subject: [PATCH 28/83] fixed bug causing sidebar to throw and error after
wiping cache
---
src/commands/refactorSmell.ts | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index b6d3bf0..384ce65 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -10,7 +10,6 @@ import { FileHighlighter } from '../ui/fileHighlighter';
import { ContextManager } from '../context/contextManager';
import { RefactorManager } from '../ui/refactorManager';
import { setTimeout } from 'timers/promises';
-import { inherits } from 'util';
export interface MultiRefactoredData {
tempDirs: string[];
@@ -70,7 +69,11 @@ export async function refactorSelectedSmell(
// Find the smell to refactor
let smellToRefactor: Smell | undefined;
if (smellGiven?.messageId) {
- smellToRefactor = smellsData.find((smell: Smell) => smell.messageId === smellGiven.messageId && smellGiven.occurences[0].line === smell.occurences[0].line);
+ smellToRefactor = smellsData.find(
+ (smell: Smell) =>
+ smell.messageId === smellGiven.messageId &&
+ smellGiven.occurences[0].line === smell.occurences[0].line
+ );
} else {
smellToRefactor = smellsData.find(
(smell: Smell) => selectedLine === smell.occurences[0].line
@@ -258,11 +261,11 @@ async function startRefactoringSession(
editor: vscode.TextEditor,
refactoredData: RefactoredData | MultiRefactoredData
) {
+ await vscode.commands.executeCommand('extension.refactorSidebar.focus');
+
// Store only the diff editor state
await contextManager.setWorkspaceData('refactorData', refactoredData);
- await vscode.commands.executeCommand('extension.refactorSidebar.focus');
-
//Read the refactored code
const refactoredCode = vscode.Uri.file(refactoredData.targetFile.refactored);
From 1b80ed12919d516af5e8ffadb2234e281cb01315 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 6 Feb 2025 15:13:53 -0500
Subject: [PATCH 29/83] changed payload to send the actual workspace folder
---
src/api/backend.ts | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/api/backend.ts b/src/api/backend.ts
index a2a4c66..1d26f90 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,5 +1,5 @@
-import * as fs from 'fs';
-import path from 'path';
+import * as vscode from 'vscode';
+
import { Smell, RefactorOutput } from '../types';
const BASE_URL = 'http://127.0.0.1:8000'; // API URL for Python backend
@@ -27,8 +27,14 @@ export async function refactorSmell(
): Promise {
const url = `${BASE_URL}/refactor`;
+ const workspace_folder = vscode.workspace.workspaceFolders?.find((folder) =>
+ filePath.includes(folder.uri.fsPath)
+ )?.uri.fsPath;
+
+ console.log(`workspace folder: ${workspace_folder}`);
+
const payload = {
- source_dir: path.dirname(filePath),
+ source_dir: workspace_folder,
smell
};
From 865006135205281212a5f2a85ae05135522e4b2e Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 6 Feb 2025 16:42:36 -0500
Subject: [PATCH 30/83] fixed refactorings not being applied to original files
---
src/commands/refactorSmell.ts | 3 +++
src/ui/refactorView.ts | 19 +++++++++++--------
2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 384ce65..5aea710 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -5,6 +5,7 @@ import { envConfig } from '../utils/envConfig';
import { getEditorAndFilePath } from '../utils/editorUtils';
import { refactorSmell } from '../api/backend';
+import { sidebarState } from '../utils/handleEditorChange';
import { FileHighlighter } from '../ui/fileHighlighter';
import { ContextManager } from '../context/contextManager';
@@ -294,6 +295,7 @@ async function startRefactoringSession(
await vscode.window.showTextDocument(doc, { preview: false });
//Show the diff viewer
+ sidebarState.isOpening = true;
vscode.commands.executeCommand(
'vscode.diff',
originalCode,
@@ -301,6 +303,7 @@ async function startRefactoringSession(
'Refactoring Comparison'
);
vscode.commands.executeCommand('ecooptimizer-vs-code-plugin.showRefactorSidebar');
+ sidebarState.isOpening = false;
}
function cleanTemps(pastData: any) {
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
index 6526332..eb9c234 100644
--- a/src/ui/refactorView.ts
+++ b/src/ui/refactorView.ts
@@ -60,10 +60,10 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
break;
case 'accept':
this.applyRefactoring();
- await this.closeViews();
+ this.closeViews();
break;
case 'reject':
- await this.closeViews();
+ this.closeViews();
break;
}
});
@@ -137,30 +137,33 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
return htmlContent;
}
- private async closeViews() {
+ private closeViews() {
console.log('Cleaning up webview');
this.clearView();
vscode.commands.executeCommand('workbench.action.closeActiveEditor');
vscode.commands.executeCommand('workbench.view.explorer');
+ this._context.workspaceState.update('activeDiff', undefined);
+
const tempDirs =
this._context.workspaceState.get('refactorData')?.tempDir! ||
this._context.workspaceState.get('refactorData')
?.tempDirs;
+ console.log(`temp dir: ${tempDirs}`);
+
if (Array.isArray(tempDirs)) {
tempDirs.forEach(async (dir) => {
- await fs.promises.rm(dir, { recursive: true });
+ fs.rmSync(dir, { recursive: true });
});
} else {
- await fs.promises.rm(tempDirs!, { recursive: true });
+ fs.rmSync(tempDirs!, { recursive: true });
}
- this._context.workspaceState.update('activeDiff', undefined);
this._context.workspaceState.update('refactorData', undefined);
}
- private applyRefactoring() {
+ private async applyRefactoring() {
this._file_map!.forEach((refactored, original) => {
vscode.window.showInformationMessage('Applying Eco changes...');
console.log(`refactored: ${refactored}\noriginal: ${original}`);
@@ -170,6 +173,6 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
fs.writeFileSync(original.fsPath, modifiedContent);
});
- vscode.window.showInformationMessage('Refactoring applied successfully!');
+ await vscode.window.showInformationMessage('Refactoring applied successfully!');
}
}
From dbe1cb90555bfd1f2dedddbdd38c9054ef9c995e Mon Sep 17 00:00:00 2001
From: tbrar06
Date: Sat, 8 Feb 2025 14:54:35 -0500
Subject: [PATCH 31/83] ssm-lab/capstone--source-code-optimizer#346 added
contributes for source code, logging and unit test directories
---
package.json | 20 +++++++++++
src/utils/configManager.ts | 72 ++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+)
create mode 100644 src/utils/configManager.ts
diff --git a/package.json b/package.json
index d612712..2e13639 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,26 @@
],
"main": "./dist/extension.js",
"contributes": {
+ "configuration": {
+ "title": "EcoOptimizer Settings",
+ "properties": {
+ "ecooptimizer-vs-code-plugin.projectWorkspacePath": {
+ "type": "string",
+ "default": "",
+ "description": "Path to the project workspace. Defaults to the currently open folder in VS Code."
+ },
+ "ecooptimizer-vs-code-plugin.unitTestPath": {
+ "type": "string",
+ "default": "",
+ "description": "Path to the unit test directory. Defaults to none."
+ },
+ "ecooptimizer-vs-code-plugin.logsOutputPath": {
+ "type": "string",
+ "default": "",
+ "description": "Path to store log files and output reports. Defaults to a 'logs' folder inside the workspace."
+ }
+ }
+ },
"commands": [
{
"command": "ecooptimizer-vs-code-plugin.detectSmells",
diff --git a/src/utils/configManager.ts b/src/utils/configManager.ts
new file mode 100644
index 0000000..06f4899
--- /dev/null
+++ b/src/utils/configManager.ts
@@ -0,0 +1,72 @@
+import * as vscode from 'vscode';
+
+export class ConfigManager {
+ // resolve ${workspaceFolder} placeholder
+ private static resolvePath(path: string): string {
+ const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
+ return path.replace('${workspaceFolder}', workspaceFolder);
+ }
+
+ // get workspace path
+ static getWorkspacePath(): string {
+ const rawPath = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin').get('projectWorkspacePath', '');
+ const resolvedPath = rawPath || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
+
+ // write to both User and Workspace settings if not already set
+ this.writeSetting('projectWorkspacePath', resolvedPath);
+
+ return resolvedPath;
+ }
+
+ // get unit test path
+ static getUnitTestPath(): string {
+ const rawPath = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin').get('unitTestPath', '');
+ const resolvedPath = this.resolvePath(rawPath);
+
+ return resolvedPath;
+ }
+
+ // get logs output path
+ static getLogsOutputPath(): string {
+ const rawPath = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin').get('logsOutputPath', '');
+ const workspacePath = this.getWorkspacePath();
+ const resolvedPath = rawPath || `${workspacePath}/logs`;
+
+ // write to both User and Workspace settings if not already set
+ this.writeSetting('logsOutputPath', resolvedPath);
+
+ return resolvedPath;
+ }
+
+ // listen for configuration changes
+ static onConfigChange(callback: () => void) {
+ vscode.workspace.onDidChangeConfiguration(event => {
+ if (
+ event.affectsConfiguration('ecooptimizer-vs-code-plugin.projectWorkspacePath') ||
+ event.affectsConfiguration('ecooptimizer-vs-code-plugin.unitTestPath') ||
+ event.affectsConfiguration('ecooptimizer-vs-code-plugin.logsOutputPath')
+ ) {
+ callback();
+ }
+ });
+ }
+
+ // write settings to both User and Workspace if necessary
+ private static writeSetting(setting: string, value: string) {
+ const config = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin');
+
+ // inspect current values in both User and Workspace settings
+ const currentValueGlobal = config.inspect(setting)?.globalValue;
+ const currentValueWorkspace = config.inspect(setting)?.workspaceValue;
+
+ // update User Settings (Global) if empty
+ if (!currentValueGlobal || currentValueGlobal.trim() === '') {
+ config.update(setting, value, vscode.ConfigurationTarget.Global);
+ }
+
+ // update Workspace Settings if empty
+ if (!currentValueWorkspace || currentValueWorkspace.trim() === '') {
+ config.update(setting, value, vscode.ConfigurationTarget.Workspace);
+ }
+ }
+}
From 9df064216eb67641e6940da1241ba80825400240 Mon Sep 17 00:00:00 2001
From: tbrar06
Date: Sat, 8 Feb 2025 18:57:48 -0500
Subject: [PATCH 32/83] ssm-lab/capstone--source-code-optimizer#346 added
dialog on activate extension to open settings if not configured
---
src/extension.ts | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/src/extension.ts b/src/extension.ts
index 6adc8de..3c503f7 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -13,6 +13,7 @@ import { handleEditorChanges } from './utils/handleEditorChange';
export function activate(context: vscode.ExtensionContext) {
console.log('Refactor Plugin activated');
+ showSettingsPopup();
const contextManager = new ContextManager(context);
@@ -150,6 +151,35 @@ export function activate(context: vscode.ExtensionContext) {
);
}
+function showSettingsPopup() {
+ // Check if the required settings are already configured
+ const config = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin');
+ const workspacePath = config.get('projectWorkspacePath', '');
+ const logsOutputPath = config.get('logsOutputPath', '');
+
+ // If settings are not configured, prompt the user to configure them
+ if (!workspacePath || !logsOutputPath) {
+ vscode.window
+ .showInformationMessage(
+ 'Please configure the paths for your workspace and logs.',
+ { modal: true },
+ 'Continue', // Button to open settings
+ 'Skip for now' // Button to dismiss
+ )
+ .then((selection) => {
+ if (selection === 'Continue') {
+ // Open the settings page filtered to your extension's settings
+ vscode.commands.executeCommand('workbench.action.openSettings', 'ecooptimizer-vs-code-plugin');
+ } else if (selection === 'Skip for now') {
+ // Inform user they can configure later
+ vscode.window.showInformationMessage(
+ 'You can configure the paths later in the settings.'
+ );
+ }
+ });
+ }
+}
+
export function deactivate() {
console.log('Refactor Plugin deactivated');
}
From ddffa99ac4403910880a12ddc8122ae9ac6be69c Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sun, 9 Feb 2025 02:39:33 -0500
Subject: [PATCH 33/83] Added functionality for filtering and showing logs
---
.env | 3 +-
package.json | 83 ++++++++++++++++-
src/api/backend.ts | 71 ++++++++++++---
src/commands/detectSmells.ts | 151 ++++++++++++++++++-------------
src/commands/showLogs.ts | 97 ++++++++++++++++++++
src/commands/wipeWorkCache.ts | 50 ++++++++--
src/extension.ts | 146 +++++++++++++++++-------------
src/utils/envConfig.ts | 4 +-
src/utils/handleSmellSettings.ts | 48 ++++++++++
9 files changed, 499 insertions(+), 154 deletions(-)
create mode 100644 src/commands/showLogs.ts
create mode 100644 src/utils/handleSmellSettings.ts
diff --git a/.env b/.env
index 2b85682..a3e7ba0 100644
--- a/.env
+++ b/.env
@@ -1,2 +1,3 @@
SMELL_MAP_KEY='workspaceSmells'
-FILE_CHANGES_KEY='lastSavedHashes'
\ No newline at end of file
+FILE_CHANGES_KEY='lastSavedHashes'
+LAST_USED_SMELLS_KEY='lastUsedSmells'
diff --git a/package.json b/package.json
index d612712..12f854c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "ecooptimizer-vs-code-plugin",
- "displayName": "ecooptimizer-vs-code-plugin",
- "description": "VS Code Plugin for Ecooptimizer Refactoring Tool",
+ "displayName": "EcoOptimizer VS Code Plugin",
+ "description": "VS Code Plugin for EcoOptimizer Refactoring Tool",
"version": "0.0.1",
"engines": {
"vscode": "^1.92.0"
@@ -34,21 +34,94 @@
"command": "ecooptimizer-vs-code-plugin.showRefactorSidebar",
"title": "Show Refactor Sidebar",
"category": "Eco",
- "enablement": "false"
+ "when": "false"
},
{
"command": "ecooptimizer-vs-code-plugin.pauseRefactorSidebar",
"title": "Pause Refactor Sidebar",
"category": "Eco",
- "enablement": "false"
+ "when": "false"
},
{
"command": "ecooptimizer-vs-code-plugin.clearRefactorSidebar",
"title": "Clear Refactor Sidebar",
"category": "Eco",
- "enablement": "false"
+ "when": "false"
+ },
+ {
+ "command": "ecooptimizer-vs-code-plugin.showLogs",
+ "title": "Show Backend Logs",
+ "category": "Eco"
}
],
+ "configuration": {
+ "title": "EcoOptimizer",
+ "properties": {
+ "ecooptimizer.enableSmells": {
+ "type": "object",
+ "additionalProperties": false,
+ "description": "Enable or disable specific code smell detections.",
+ "default": {
+ "long-element-chain": true,
+ "too-many-arguments": true,
+ "long-lambda-expression": true,
+ "long-message-chain": true,
+ "unused-variables-and-attributes": true,
+ "cached-repeated-calls": true,
+ "string-concat-loop": true,
+ "no-self-use": true,
+ "use-a-generator": true
+ },
+ "properties": {
+ "long-element-chain": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of Long Element Chain"
+ },
+ "too-many-arguments": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of Long Parameter List"
+ },
+ "long-lambda-expression": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of Long Lambda Expression"
+ },
+ "long-message-chain": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of Long Message Chain"
+ },
+ "unused-variables-and-attributes": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of Unused Variables and Attributes"
+ },
+ "cached-repeated-calls": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of Cached Repeated Calls"
+ },
+ "string-concat-loop": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of String Concatenation in Loop"
+ },
+ "no-self-use": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of No Self Use"
+ },
+ "use-a-generator": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of Use a Generator"
+ }
+ }
+ }
+ }
+ },
"keybindings": [
{
"command": "ecooptimizer-vs-code-plugin.refactorSmell",
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 1d26f90..1c50d38 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,22 +1,55 @@
import * as vscode from 'vscode';
-import { Smell, RefactorOutput } from '../types';
+import { Smell } from '../types';
const BASE_URL = 'http://127.0.0.1:8000'; // API URL for Python backend
-// Fetch detected smells for a given file
-export async function fetchSmells(filePath: string): Promise {
- const url = `${BASE_URL}/smells?file_path=${encodeURIComponent(filePath)}`;
+// β
Fetch detected smells for a given file (only enabled smells)
+export async function fetchSmells(
+ filePath: string,
+ enabledSmells: string[]
+): Promise {
+ const url = `${BASE_URL}/smells`;
+
try {
- const response = await fetch(url);
+ console.log(
+ `Eco: Requesting smells for file: ${filePath} with filters: ${enabledSmells}`
+ );
+
+ const response = await fetch(url, {
+ method: 'POST', // β
Send enabled smells in the request body
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ file_path: filePath, enabled_smells: enabledSmells }) // β
Include enabled smells
+ });
+
if (!response.ok) {
- throw new Error(`Error fetching smells: ${response.statusText}`);
+ console.error(
+ `Eco: API request failed (${response.status} - ${response.statusText})`
+ );
+ vscode.window.showErrorMessage(
+ `Eco: Failed to fetch smells (HTTP ${response.status})`
+ );
+ return [];
}
+
const smellsList = (await response.json()) as Smell[];
+
+ if (!Array.isArray(smellsList)) {
+ console.error('Eco: Invalid response format from backend.');
+ vscode.window.showErrorMessage('Eco: Unexpected response from backend.');
+ return [];
+ }
+
+ console.log(`Eco: Successfully retrieved ${smellsList.length} smells.`);
return smellsList;
- } catch (error) {
- console.error('Error in getSmells:', error);
- throw error;
+ } catch (error: any) {
+ console.error(`Eco: Network error while fetching smells: ${error.message}`);
+ vscode.window.showErrorMessage(
+ 'Eco: Unable to reach the backend. Please check your connection.'
+ );
+ return [];
}
}
@@ -31,7 +64,16 @@ export async function refactorSmell(
filePath.includes(folder.uri.fsPath)
)?.uri.fsPath;
- console.log(`workspace folder: ${workspace_folder}`);
+ if (!workspace_folder) {
+ console.error('Eco: Error - Unable to determine workspace folder for', filePath);
+ throw new Error(
+ `Eco: Unable to find a matching workspace folder for file: ${filePath}`
+ );
+ }
+
+ console.log(
+ `Eco: Initiating refactoring for smell "${smell.symbol}" in "${workspace_folder}"`
+ );
const payload = {
source_dir: workspace_folder,
@@ -48,13 +90,18 @@ export async function refactorSmell(
});
if (!response.ok) {
- throw new Error(`Error refactoring smell: ${await response.text()}`);
+ const errorText = await response.text();
+ console.error(
+ `Eco: Error - Refactoring smell "${smell.symbol}": ${errorText}`
+ );
+ throw new Error(`Eco: Error refactoring smell: ${errorText}`);
}
const refactorResult = (await response.json()) as RefactorOutput;
+ console.log(`Eco: Refactoring successful for smell "${smell.symbol}"`);
return refactorResult;
} catch (error) {
- console.error('Error in refactorSmell:', error);
+ console.error('Eco: Unexpected error in refactorSmell:', error);
throw error;
}
}
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index ebdfe7d..4d61186 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -1,13 +1,12 @@
import * as vscode from 'vscode';
import { FileHighlighter } from '../ui/fileHighlighter';
import { getEditorAndFilePath } from '../utils/editorUtils';
-import * as fs from 'fs';
-import { Smell } from '../types';
import { fetchSmells } from '../api/backend';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
import { hashContent, updateHash } from '../utils/hashDocs';
-// import { HoverManager } from "../ui/hoverManager"; // Import the HoverManager
+import { wipeWorkCache } from './wipeWorkCache'; // β
Import cache wipe function
+import { Smell } from '../types';
export interface SmellDetectRecord {
hash: string;
@@ -16,102 +15,126 @@ export interface SmellDetectRecord {
let fileHighlighter: FileHighlighter;
-export async function getSmells(filePath: string, contextManager: ContextManager) {
- try {
- const smellsList: Smell[] = await fetchSmells(filePath);
- if (smellsList.length === 0) {
- throw new Error('Detected smells data is invalid or empty.');
- }
-
- return smellsList;
- } catch (error) {
- console.error('Error detecting smells:', error);
- vscode.window.showErrorMessage(`Eco: Error detecting smells: ${error}`);
- return;
- }
-}
-
export async function detectSmells(contextManager: ContextManager) {
const { editor, filePath } = getEditorAndFilePath();
+ // β
Ensure an active editor exists
if (!editor) {
- vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as no active editor found.'
- );
- console.error('No active editor found to detect smells. Returning back.');
+ vscode.window.showErrorMessage('Eco: No active editor found.');
+ console.error('Eco: No active editor found to detect smells.');
return;
}
+
+ // β
Ensure filePath is valid
if (!filePath) {
- vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as active editor does not have a valid file path.'
- );
- console.error('No valid file path found to detect smells. Returning back.');
+ vscode.window.showErrorMessage('Eco: Active editor has no valid file path.');
+ console.error('Eco: No valid file path found for smell detection.');
return;
}
- vscode.window.showInformationMessage('Eco: Detecting smells...');
- console.log('Detecting smells in detectSmells');
+ console.log(`Eco: Detecting smells in file: ${filePath}`);
- let smellsData: Smell[] | undefined;
+ // β
Fetch user-enabled smells
+ const enabledSmells = getEnabledSmells();
+ const activeSmells = Object.keys(enabledSmells).filter(
+ (smell) => enabledSmells[smell]
+ );
+
+ if (activeSmells.length === 0) {
+ vscode.window.showWarningMessage(
+ 'Eco: No smells are enabled! Detection skipped.'
+ );
+ console.warn('Eco: No smells are enabled. Detection will not proceed.');
+ return;
+ }
- // Get the stored smells and current file hash
- const allSmells = contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!
- ) as Record;
+ // β
Check if the enabled smells have changed
+ const lastUsedSmells = contextManager.getWorkspaceData(
+ envConfig.LAST_USED_SMELLS_KEY!,
+ {}
+ );
+ if (JSON.stringify(lastUsedSmells) !== JSON.stringify(enabledSmells)) {
+ console.log('Eco: Smell settings have changed! Wiping cache.');
+ await wipeWorkCache(contextManager, 'settings');
+ contextManager.setWorkspaceData(envConfig.LAST_USED_SMELLS_KEY!, enabledSmells);
+ }
- const fileSmells = allSmells[filePath];
+ // β
Retrieve cached smells
+ const allSmells: Record =
+ contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!) || {};
+ const fileSmells = allSmells[filePath];
const currentFileHash = hashContent(editor.document.getText());
- // Function to handle the smells data retrieval and updating
+ // β
Function to fetch smells and update cache
async function fetchAndStoreSmells(): Promise {
- smellsData = await getSmells(filePath!, contextManager);
+ console.log(
+ `Eco: Fetching smells from backend for file: ${filePath} with filters: ${activeSmells}`
+ );
- if (!smellsData) {
- console.log('No valid smells data found. Returning.');
- vscode.window.showErrorMessage('Eco: No smells are present in current file.');
- return undefined; // Indicate failure to fetch smells
+ if (!filePath) {
+ console.error(`Eco: File path is undefined when fetching smells.`);
+ return undefined;
}
- allSmells[filePath!] = {
- hash: currentFileHash,
- smells: smellsData
- };
+ const smellsData = await fetchSmells(filePath, activeSmells);
+
+ if (!smellsData || smellsData.length === 0) {
+ console.log(`Eco: No smells found in file: ${filePath}`);
+ vscode.window.showInformationMessage('Eco: No code smells detected.');
+ return [];
+ }
+
+ console.log(
+ `Eco: ${smellsData.length} smells found in ${filePath}. Updating cache.`
+ );
+
+ // β
Ensure safe update of smells cache
+ allSmells[filePath] = { hash: currentFileHash, smells: smellsData };
contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, allSmells);
- return smellsData; // Successfully fetched and stored smells
+ return smellsData;
}
- if (fileSmells) {
- if (currentFileHash === fileSmells.hash) {
- smellsData = fileSmells.smells;
+ let smellsData: Smell[] | undefined;
+
+ // β
**Check cache before requesting backend**
+ if (fileSmells && currentFileHash === fileSmells.hash) {
+ console.log(`Eco: Using cached smells for ${filePath}`);
+ vscode.window.showInformationMessage(`Eco: Using cached smells for ${filePath}`);
+ smellsData = fileSmells.smells;
+ } else {
+ if (fileSmells) {
+ console.log(`Eco: File changed. Updating smells.`);
+ await wipeWorkCache(contextManager, 'fileChange');
} else {
- console.log('Updating smells');
- smellsData = await fetchAndStoreSmells();
- if (!smellsData) {
- return;
- }
+ console.log(`Eco: No cached smells found. Fetching from backend.`);
}
- } else {
+
updateHash(contextManager, editor.document);
smellsData = await fetchAndStoreSmells();
- if (!smellsData) {
- return;
- }
}
- console.log('Saving smells to workspace data.');
-
- vscode.window.showInformationMessage(
- `Eco: Detected ${smellsData.length} smells in the file.`
- );
+ if (!smellsData || smellsData.length === 0) {
+ console.log(`Eco: No smells to highlight for ${filePath}.`);
+ return;
+ }
+ // β
Highlight smells in editor
+ console.log(`Eco: Highlighting detected smells in ${filePath}.`);
if (!fileHighlighter) {
fileHighlighter = new FileHighlighter(contextManager);
}
- // const hoverManager = new HoverManager(context, smellsData);
fileHighlighter.highlightSmells(editor, smellsData);
+
vscode.window.showInformationMessage(
- 'Eco: Detected code smells have been highlighted.'
+ `Eco: Highlighted ${smellsData.length} smells.`
);
}
+
+/**
+ * β
Fetches the currently enabled smells from VS Code settings.
+ */
+function getEnabledSmells(): { [key: string]: boolean } {
+ return vscode.workspace.getConfiguration('ecooptimizer').get('enableSmells', {});
+}
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
new file mode 100644
index 0000000..60b146e
--- /dev/null
+++ b/src/commands/showLogs.ts
@@ -0,0 +1,97 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import * as path from 'path';
+
+/**
+ * Returns the EcoOptimizer log directory inside the user's home directory.
+ */
+function getLogDirectory(): string {
+ const userHome = process.env.HOME || process.env.USERPROFILE;
+ if (!userHome) {
+ vscode.window.showErrorMessage('Eco: Unable to determine user home directory.');
+ return '';
+ }
+ return path.join(userHome, '.ecooptimizer', 'outputs', 'logs');
+}
+
+/**
+ * Defines log file paths dynamically based on the home directory.
+ */
+function getLogFiles(): Record {
+ const LOG_DIR = getLogDirectory();
+ return {
+ 'Main Log': path.join(LOG_DIR, 'main.log'),
+ 'Detect Smells Log': path.join(LOG_DIR, 'detect_smells.log'),
+ 'Refactor Smell Log': path.join(LOG_DIR, 'refactor_smell.log')
+ };
+}
+
+// β
Create an output channel for logs
+let outputChannel = vscode.window.createOutputChannel('Eco Optimizer Logs');
+
+/**
+ * Registers the command to show logs in VS Code.
+ */
+export function showLogsCommand(context: vscode.ExtensionContext) {
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.showLogs',
+ async () => {
+ const LOG_FILES = getLogFiles();
+ if (!LOG_FILES['Main Log']) {
+ vscode.window.showErrorMessage('Eco: Log directory is not set.');
+ return;
+ }
+
+ const selectedLog: string | undefined = await vscode.window.showQuickPick(
+ Object.keys(LOG_FILES),
+ {
+ placeHolder: 'Select a log file to view'
+ }
+ );
+
+ if (selectedLog) {
+ showLogFile(LOG_FILES[selectedLog], selectedLog);
+ }
+ }
+ )
+ );
+}
+
+/**
+ * Displays the log file content in VS Code's Output Panel.
+ */
+function showLogFile(filePath: string, logName: string) {
+ outputChannel.clear();
+ outputChannel.show();
+ outputChannel.appendLine(`π Viewing: ${logName}`);
+
+ if (!fs.existsSync(filePath)) {
+ outputChannel.appendLine('β οΈ Log file does not exist.');
+ return;
+ }
+
+ fs.readFile(filePath, 'utf8', (err, data) => {
+ if (!err) {
+ outputChannel.append(data);
+ }
+ });
+
+ // β
Watch the log file for live updates
+ fs.watchFile(filePath, { interval: 1000 }, () => {
+ fs.readFile(filePath, 'utf8', (err, data) => {
+ if (!err) {
+ outputChannel.clear();
+ outputChannel.appendLine(`π Viewing: ${logName}`);
+ outputChannel.append(data);
+ }
+ });
+ });
+}
+
+/**
+ * Stops watching log files when the extension is deactivated.
+ */
+export function stopWatchingLogs() {
+ Object.values(getLogFiles()).forEach((filePath) => fs.unwatchFile(filePath));
+}
diff --git a/src/commands/wipeWorkCache.ts b/src/commands/wipeWorkCache.ts
index 645ed81..dec59db 100644
--- a/src/commands/wipeWorkCache.ts
+++ b/src/commands/wipeWorkCache.ts
@@ -1,18 +1,50 @@
import * as vscode from 'vscode';
-
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
import { updateHash } from '../utils/hashDocs';
-export async function wipeWorkCache(contextManager: ContextManager) {
- contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, {});
- contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, {});
+export async function wipeWorkCache(
+ contextManager: ContextManager,
+ reason?: string
+) {
+ try {
+ console.log('Eco: Wiping workspace cache...');
+
+ // β
Clear stored smells cache
+ await contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, {});
+
+ // β
Update file hashes for all open editors
+ const openEditors = vscode.window.visibleTextEditors;
+
+ if (openEditors.length === 0) {
+ console.log('Eco: No open files to update hash.');
+ } else {
+ console.log(`Eco: Updating cache for ${openEditors.length} open files.`);
+ }
+
+ for (const editor of openEditors) {
+ if (editor.document) {
+ await updateHash(contextManager, editor.document);
+ }
+ }
- vscode.window.visibleTextEditors.forEach(async (editor) => {
- if (editor.document) {
- await updateHash(contextManager, editor.document);
+ // β
Determine the appropriate message
+ let message = 'Eco: Successfully wiped workspace cache! β
';
+ if (reason === 'settings') {
+ message =
+ 'Eco: Smell detection settings changed. Cache wiped to apply updates. β
';
+ } else if (reason === 'fileChange') {
+ message = 'Eco: File changed. Cache wiped to refresh smell detection. π';
+ } else if (reason === 'manual') {
+ message = 'Eco: Workspace cache manually wiped by user. β
';
}
- });
- vscode.window.showInformationMessage('Workspace cache clean!');
+ vscode.window.showInformationMessage(message);
+ console.log('Eco:', message);
+ } catch (error: any) {
+ console.error('Eco: Error while wiping workspace cache:', error);
+ vscode.window.showErrorMessage(
+ `Eco: Failed to wipe workspace cache. See console for details.`
+ );
+ }
}
diff --git a/src/extension.ts b/src/extension.ts
index 6adc8de..828c539 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,86 +1,95 @@
-import { envConfig } from './utils/envConfig'; // ENV variables should always be first import!!
-
+import { envConfig } from './utils/envConfig';
import * as vscode from 'vscode';
import { detectSmells } from './commands/detectSmells';
-import { refactorSelectedSmell } from './commands/refactorSmell';
-import { LineSelectionManager } from './ui/lineSelectionManager';
-import { ContextManager } from './context/contextManager';
+import {
+ refactorSelectedSmell,
+ refactorAllSmellsOfType
+} from './commands/refactorSmell';
import { wipeWorkCache } from './commands/wipeWorkCache';
+import { showLogsCommand, stopWatchingLogs } from './commands/showLogs';
+import { ContextManager } from './context/contextManager';
+import {
+ getEnabledSmells,
+ handleSmellFilterUpdate
+} from './utils/handleSmellSettings';
import { updateHash } from './utils/hashDocs';
import { RefactorSidebarProvider } from './ui/refactorView';
import { handleEditorChanges } from './utils/handleEditorChange';
+import { LineSelectionManager } from './ui/lineSelectionManager';
export function activate(context: vscode.ExtensionContext) {
- console.log('Refactor Plugin activated');
+ console.log('Eco: Refactor Plugin Activated Successfully');
const contextManager = new ContextManager(context);
- // ===============================================================
- // INITIALIZE WORKSPACE DATA
- // ===============================================================
-
- let smellsData = contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!);
- if (!smellsData) {
- smellsData = {};
- contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, smellsData);
- }
-
- let fileHashes = contextManager.getWorkspaceData(envConfig.FILE_CHANGES_KEY!);
- if (!fileHashes) {
- fileHashes = {};
- contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, fileHashes);
- }
+ let smellsData = contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!) || {};
+ contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, smellsData);
- // console.log(
- // `Smell detection map: ${contextManager.getWorkspaceData(
- // envConfig.SMELL_MAP_KEY!
- // )}`
- // );
+ let fileHashes =
+ contextManager.getWorkspaceData(envConfig.FILE_CHANGES_KEY!) || {};
+ contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, fileHashes);
// ===============================================================
// REGISTER COMMANDS
// ===============================================================
- // Register Detect Smells Command
- let detectSmellsCmd = vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.detectSmells',
- async () => {
- console.log('Command detectSmells triggered');
- detectSmells(contextManager);
- }
+ // Detect Smells Command
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.detectSmells',
+ async () => {
+ console.log('Eco: Detect Smells Command Triggered');
+ detectSmells(contextManager);
+ }
+ )
);
- context.subscriptions.push(detectSmellsCmd);
-
- // Register Refactor Smell Command
- let refactorSmellCmd = vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.refactorSmell',
- () => {
- console.log('Command refactorSmells triggered');
- vscode.window.showInformationMessage('Eco: Detecting smells...');
- refactorSelectedSmell(contextManager);
- }
+
+ // Refactor Selected Smell Command
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.refactorSmell',
+ () => {
+ console.log('Eco: Refactor Selected Smell Command Triggered');
+ refactorSelectedSmell(contextManager);
+ }
+ )
);
- context.subscriptions.push(refactorSmellCmd);
-
- // Register Wipe Workspace Cache
- let wipeWorkCacheCmd = vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.wipeWorkCache',
- () => {
- console.log('Command wipeWorkCache triggered');
- vscode.window.showInformationMessage(
- 'Eco: Wiping existing worspace memory...'
- );
- wipeWorkCache(contextManager);
- }
+
+ // Refactor All Smells of Type Command
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.refactorAllSmellsOfType',
+ async (smellId: string) => {
+ console.log(
+ `Eco: Refactor All Smells of Type Command Triggered for ${smellId}`
+ );
+ refactorAllSmellsOfType(contextManager, smellId);
+ }
+ )
+ );
+
+ // Wipe Cache Command
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.wipeWorkCache',
+ async () => {
+ console.log('Eco: Wipe Work Cache Command Triggered');
+ vscode.window.showInformationMessage(
+ 'Eco: Manually wiping workspace memory... β
'
+ );
+ await wipeWorkCache(contextManager, 'manual');
+ }
+ )
);
- context.subscriptions.push(wipeWorkCacheCmd);
+
+ // Log Viewing Command
+ showLogsCommand(context);
// ===============================================================
// REGISTER VIEWS
// ===============================================================
- // Register the webview provider for the refactoring webview
const refactorProvider = new RefactorSidebarProvider(context);
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(
@@ -122,8 +131,7 @@ export function activate(context: vscode.ExtensionContext) {
const lineSelectManager = new LineSelectionManager(contextManager);
context.subscriptions.push(
vscode.window.onDidChangeTextEditorSelection((event) => {
- console.log(`Detected line selection event`);
-
+ console.log('Eco: Detected line selection event');
lineSelectManager.commentLine(event.textEditor);
})
);
@@ -135,21 +143,35 @@ export function activate(context: vscode.ExtensionContext) {
})
);
- // Handles case of documents already being open on vscode open
+ // Handles case of documents already being open on VS Code open
vscode.window.visibleTextEditors.forEach(async (editor) => {
if (editor.document) {
await updateHash(contextManager, editor.document);
}
});
- // Initializes first state of document when opened while extension active
+ // Initializes first state of document when opened while extension is active
context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument(
async (document) => await updateHash(contextManager, document)
)
);
+
+ // ===============================================================
+ // HANDLE SMELL FILTER CHANGES
+ // ===============================================================
+
+ let previousSmells = getEnabledSmells();
+ vscode.workspace.onDidChangeConfiguration((event) => {
+ if (event.affectsConfiguration('ecooptimizer.enableSmells')) {
+ console.log('Eco: Smell preferences changed! Wiping cache.');
+ handleSmellFilterUpdate(previousSmells, contextManager);
+ previousSmells = getEnabledSmells();
+ }
+ });
}
export function deactivate() {
- console.log('Refactor Plugin deactivated');
+ console.log('Eco: Deactivating Plugin - Stopping Log Watching');
+ stopWatchingLogs();
}
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts
index a4b36ce..bc35265 100644
--- a/src/utils/envConfig.ts
+++ b/src/utils/envConfig.ts
@@ -5,9 +5,11 @@ dotenv.config();
export interface EnvConfig {
SMELL_MAP_KEY?: string;
FILE_CHANGES_KEY?: string;
+ LAST_USED_SMELLS_KEY?: string;
}
export const envConfig: EnvConfig = {
SMELL_MAP_KEY: process.env.SMELL_MAP_KEY,
- FILE_CHANGES_KEY: process.env.FILE_CHANGES_KEY
+ FILE_CHANGES_KEY: process.env.FILE_CHANGES_KEY,
+ LAST_USED_SMELLS_KEY: process.env.LAST_USED_SMELLS_KEY
};
diff --git a/src/utils/handleSmellSettings.ts b/src/utils/handleSmellSettings.ts
new file mode 100644
index 0000000..7660b4e
--- /dev/null
+++ b/src/utils/handleSmellSettings.ts
@@ -0,0 +1,48 @@
+import * as vscode from 'vscode';
+import { wipeWorkCache } from '../commands/wipeWorkCache';
+import { ContextManager } from '../context/contextManager';
+
+/**
+ * Fetches the current enabled smells from VS Code settings.
+ */
+export function getEnabledSmells(): { [key: string]: boolean } {
+ return vscode.workspace.getConfiguration('ecooptimizer').get('enableSmells', {});
+}
+
+/**
+ * Handles when a user updates the smell filter in settings.
+ * It wipes the cache and notifies the user about changes.
+ */
+export function handleSmellFilterUpdate(
+ previousSmells: { [key: string]: boolean },
+ contextManager: ContextManager
+) {
+ const currentSmells = getEnabledSmells();
+ let smellsChanged = false;
+
+ Object.entries(currentSmells).forEach(([smell, isEnabled]) => {
+ if (previousSmells[smell] !== isEnabled) {
+ smellsChanged = true;
+ vscode.window.showInformationMessage(
+ isEnabled
+ ? `Eco: Enabled detection of ${formatSmellName(smell)}.`
+ : `Eco: Disabled detection of ${formatSmellName(smell)}.`
+ );
+ }
+ });
+
+ // If any smell preference changed, wipe the cache
+ if (smellsChanged) {
+ console.log('Eco: Smell preferences changed! Wiping cache.');
+ wipeWorkCache(contextManager, 'settings');
+ }
+}
+
+/**
+ * Formats the smell name from kebab-case to a readable format.
+ */
+export function formatSmellName(smellKey: string): string {
+ return smellKey
+ .replace(/-/g, ' ') // Replace hyphens with spaces
+ .replace(/\b\w/g, (char) => char.toUpperCase()); // Capitalize first letter
+}
From 6a0c860d6c922f0bd4e90b6573d2746f17e098a4 Mon Sep 17 00:00:00 2001
From: tbrar06
Date: Sun, 9 Feb 2025 08:07:05 -0500
Subject: [PATCH 34/83] ssm-lab/capstone--source-code-optimizer#346 added
warning dialog for changing config path
---
src/extension.ts | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/extension.ts b/src/extension.ts
index 3c503f7..5299bda 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -13,8 +13,16 @@ import { handleEditorChanges } from './utils/handleEditorChange';
export function activate(context: vscode.ExtensionContext) {
console.log('Refactor Plugin activated');
+
+ // Show the settings popup if needed
showSettingsPopup();
+ // Register a listener for configuration changes
+ context.subscriptions.push(
+ vscode.workspace.onDidChangeConfiguration((event) => {
+ handleConfigurationChange(event);
+ })
+ );
const contextManager = new ContextManager(context);
// ===============================================================
@@ -180,6 +188,21 @@ function showSettingsPopup() {
}
}
+function handleConfigurationChange(event: vscode.ConfigurationChangeEvent) {
+ // Check if any relevant setting was changed
+ if (
+ event.affectsConfiguration('ecooptimizer-vs-code-plugin.projectWorkspacePath') ||
+ event.affectsConfiguration('ecooptimizer-vs-code-plugin.unitTestPath') ||
+ event.affectsConfiguration('ecooptimizer-vs-code-plugin.logsOutputPath')
+ ) {
+ // Display a warning message about changing critical settings
+ vscode.window.showWarningMessage(
+ 'You have changed a critical setting for the EcoOptimizer plugin. Ensure the new value is valid and correct for optimal functionality.'
+ );
+ }
+}
+
+
export function deactivate() {
console.log('Refactor Plugin deactivated');
}
From e4e618dec2f00e49fb70f04c484f4f759c534999 Mon Sep 17 00:00:00 2001
From: tbrar06
Date: Sun, 9 Feb 2025 08:49:31 -0500
Subject: [PATCH 35/83] ssm-lab/capstone--source-code-optimizer#346 added
reminder for missing unit test path
---
src/extension.ts | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/extension.ts b/src/extension.ts
index 5299bda..3753ca0 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -164,9 +164,10 @@ function showSettingsPopup() {
const config = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin');
const workspacePath = config.get('projectWorkspacePath', '');
const logsOutputPath = config.get('logsOutputPath', '');
+ const unitTestPath = config.get('unitTestPath', '');
// If settings are not configured, prompt the user to configure them
- if (!workspacePath || !logsOutputPath) {
+ if (!workspacePath || !logsOutputPath || !unitTestPath) {
vscode.window
.showInformationMessage(
'Please configure the paths for your workspace and logs.',
@@ -176,7 +177,7 @@ function showSettingsPopup() {
)
.then((selection) => {
if (selection === 'Continue') {
- // Open the settings page filtered to your extension's settings
+ // Open the settings page filtered to extension's settings
vscode.commands.executeCommand('workbench.action.openSettings', 'ecooptimizer-vs-code-plugin');
} else if (selection === 'Skip for now') {
// Inform user they can configure later
From 8e012ce13110e679db1cb84e4fe9fb8d8a14b54f Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sun, 9 Feb 2025 12:04:46 -0500
Subject: [PATCH 36/83] Edited some setting configs
ssm-lab/capstone--source-code-optimizer#346
---
package.json | 8 ++++----
src/extension.ts | 12 +++++++-----
src/utils/configManager.ts | 23 ++++++++++++++++-------
3 files changed, 27 insertions(+), 16 deletions(-)
diff --git a/package.json b/package.json
index 2e13639..663f8d9 100644
--- a/package.json
+++ b/package.json
@@ -15,17 +15,17 @@
"main": "./dist/extension.js",
"contributes": {
"configuration": {
- "title": "EcoOptimizer Settings",
+ "title": "EcoOptimizer",
"properties": {
"ecooptimizer-vs-code-plugin.projectWorkspacePath": {
"type": "string",
"default": "",
- "description": "Path to the project workspace. Defaults to the currently open folder in VS Code."
+ "description": "Path to the folder to be targeted, relative to current workspace. Defaults to the currently open folder in VS Code."
},
- "ecooptimizer-vs-code-plugin.unitTestPath": {
+ "ecooptimizer-vs-code-plugin.unitTestCommand": {
"type": "string",
"default": "",
- "description": "Path to the unit test directory. Defaults to none."
+ "description": "Command to run unit tests. Should be the same as if written in the terminal (e.i. pytest tests/)."
},
"ecooptimizer-vs-code-plugin.logsOutputPath": {
"type": "string",
diff --git a/src/extension.ts b/src/extension.ts
index 3753ca0..f301ba9 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -166,19 +166,22 @@ function showSettingsPopup() {
const logsOutputPath = config.get('logsOutputPath', '');
const unitTestPath = config.get('unitTestPath', '');
- // If settings are not configured, prompt the user to configure them
+ // If settings are not configured, prompt the user to configure them
if (!workspacePath || !logsOutputPath || !unitTestPath) {
vscode.window
.showInformationMessage(
'Please configure the paths for your workspace and logs.',
- { modal: true },
+ { modal: true },
'Continue', // Button to open settings
'Skip for now' // Button to dismiss
)
.then((selection) => {
if (selection === 'Continue') {
// Open the settings page filtered to extension's settings
- vscode.commands.executeCommand('workbench.action.openSettings', 'ecooptimizer-vs-code-plugin');
+ vscode.commands.executeCommand(
+ 'workbench.action.openSettings',
+ 'ecooptimizer-vs-code-plugin'
+ );
} else if (selection === 'Skip for now') {
// Inform user they can configure later
vscode.window.showInformationMessage(
@@ -193,7 +196,7 @@ function handleConfigurationChange(event: vscode.ConfigurationChangeEvent) {
// Check if any relevant setting was changed
if (
event.affectsConfiguration('ecooptimizer-vs-code-plugin.projectWorkspacePath') ||
- event.affectsConfiguration('ecooptimizer-vs-code-plugin.unitTestPath') ||
+ event.affectsConfiguration('ecooptimizer-vs-code-plugin.unitTestCommand') ||
event.affectsConfiguration('ecooptimizer-vs-code-plugin.logsOutputPath')
) {
// Display a warning message about changing critical settings
@@ -203,7 +206,6 @@ function handleConfigurationChange(event: vscode.ConfigurationChangeEvent) {
}
}
-
export function deactivate() {
console.log('Refactor Plugin deactivated');
}
diff --git a/src/utils/configManager.ts b/src/utils/configManager.ts
index 06f4899..1298152 100644
--- a/src/utils/configManager.ts
+++ b/src/utils/configManager.ts
@@ -9,8 +9,11 @@ export class ConfigManager {
// get workspace path
static getWorkspacePath(): string {
- const rawPath = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin').get('projectWorkspacePath', '');
- const resolvedPath = rawPath || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
+ const rawPath = vscode.workspace
+ .getConfiguration('ecooptimizer-vs-code-plugin')
+ .get('projectWorkspacePath', '');
+ const resolvedPath =
+ rawPath || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
// write to both User and Workspace settings if not already set
this.writeSetting('projectWorkspacePath', resolvedPath);
@@ -20,7 +23,9 @@ export class ConfigManager {
// get unit test path
static getUnitTestPath(): string {
- const rawPath = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin').get('unitTestPath', '');
+ const rawPath = vscode.workspace
+ .getConfiguration('ecooptimizer-vs-code-plugin')
+ .get('unitTestPath', '');
const resolvedPath = this.resolvePath(rawPath);
return resolvedPath;
@@ -28,7 +33,9 @@ export class ConfigManager {
// get logs output path
static getLogsOutputPath(): string {
- const rawPath = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin').get('logsOutputPath', '');
+ const rawPath = vscode.workspace
+ .getConfiguration('ecooptimizer-vs-code-plugin')
+ .get('logsOutputPath', '');
const workspacePath = this.getWorkspacePath();
const resolvedPath = rawPath || `${workspacePath}/logs`;
@@ -40,10 +47,12 @@ export class ConfigManager {
// listen for configuration changes
static onConfigChange(callback: () => void) {
- vscode.workspace.onDidChangeConfiguration(event => {
+ vscode.workspace.onDidChangeConfiguration((event) => {
if (
- event.affectsConfiguration('ecooptimizer-vs-code-plugin.projectWorkspacePath') ||
- event.affectsConfiguration('ecooptimizer-vs-code-plugin.unitTestPath') ||
+ event.affectsConfiguration(
+ 'ecooptimizer-vs-code-plugin.projectWorkspacePath'
+ ) ||
+ event.affectsConfiguration('ecooptimizer-vs-code-plugin.unitTestCommand') ||
event.affectsConfiguration('ecooptimizer-vs-code-plugin.logsOutputPath')
) {
callback();
From 569e72b000a9b5c8ab259534c0efc5cb6de7eb16 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sun, 9 Feb 2025 21:07:19 -0500
Subject: [PATCH 37/83] Fixed refactor error msgs and made logging automatic
---
package.json | 64 ++++++++++-----------
src/api/backend.ts | 1 -
src/commands/refactorSmell.ts | 10 ++--
src/commands/showLogs.ts | 103 ++++++++++++++--------------------
src/extension.ts | 15 ++++-
5 files changed, 90 insertions(+), 103 deletions(-)
diff --git a/package.json b/package.json
index f57012f..1faeb1a 100644
--- a/package.json
+++ b/package.json
@@ -14,26 +14,6 @@
],
"main": "./dist/extension.js",
"contributes": {
- "configuration": {
- "title": "EcoOptimizer",
- "properties": {
- "ecooptimizer-vs-code-plugin.projectWorkspacePath": {
- "type": "string",
- "default": "",
- "description": "Path to the folder to be targeted, relative to current workspace. Defaults to the currently open folder in VS Code."
- },
- "ecooptimizer-vs-code-plugin.unitTestCommand": {
- "type": "string",
- "default": "",
- "description": "Command to run unit tests. Should be the same as if written in the terminal (e.i. pytest tests/)."
- },
- "ecooptimizer-vs-code-plugin.logsOutputPath": {
- "type": "string",
- "default": "",
- "description": "Path to store log files and output reports. Defaults to a 'logs' folder inside the workspace."
- }
- }
- },
"commands": [
{
"command": "ecooptimizer-vs-code-plugin.detectSmells",
@@ -54,29 +34,45 @@
"command": "ecooptimizer-vs-code-plugin.showRefactorSidebar",
"title": "Show Refactor Sidebar",
"category": "Eco",
- "when": "false"
+ "enablement": "false"
},
{
"command": "ecooptimizer-vs-code-plugin.pauseRefactorSidebar",
"title": "Pause Refactor Sidebar",
"category": "Eco",
- "when": "false"
+ "enablement": "false"
},
{
"command": "ecooptimizer-vs-code-plugin.clearRefactorSidebar",
"title": "Clear Refactor Sidebar",
"category": "Eco",
- "when": "false"
+ "enablement": "false"
},
{
- "command": "ecooptimizer-vs-code-plugin.showLogs",
+ "command": "ecooptimizer-vs-code-plugin.startLogging",
"title": "Show Backend Logs",
- "category": "Eco"
+ "category": "Eco",
+ "enablement": "false"
}
],
"configuration": {
"title": "EcoOptimizer",
"properties": {
+ "ecooptimizer-vs-code-plugin.projectWorkspacePath": {
+ "type": "string",
+ "default": "",
+ "description": "Path to the folder to be targeted, relative to current workspace. Defaults to the currently open folder in VS Code."
+ },
+ "ecooptimizer-vs-code-plugin.unitTestCommand": {
+ "type": "string",
+ "default": "",
+ "description": "Command to run unit tests. Should be the same as if written in the terminal (e.i. pytest tests/)."
+ },
+ "ecooptimizer-vs-code-plugin.logsOutputPath": {
+ "type": "string",
+ "default": "",
+ "description": "Path to store log files and output reports. Defaults to a 'logs' folder inside the workspace."
+ },
"ecooptimizer.enableSmells": {
"type": "object",
"additionalProperties": false,
@@ -96,47 +92,47 @@
"long-element-chain": {
"type": "boolean",
"default": true,
- "description": "Enable detection of Long Element Chain"
+ "description": "Long Element Chain"
},
"too-many-arguments": {
"type": "boolean",
"default": true,
- "description": "Enable detection of Long Parameter List"
+ "description": "Long Parameter List"
},
"long-lambda-expression": {
"type": "boolean",
"default": true,
- "description": "Enable detection of Long Lambda Expression"
+ "description": "Long Lambda Expression"
},
"long-message-chain": {
"type": "boolean",
"default": true,
- "description": "Enable detection of Long Message Chain"
+ "description": "Long Message Chain"
},
"unused-variables-and-attributes": {
"type": "boolean",
"default": true,
- "description": "Enable detection of Unused Variables and Attributes"
+ "description": "Unused Variables and Attributes"
},
"cached-repeated-calls": {
"type": "boolean",
"default": true,
- "description": "Enable detection of Cached Repeated Calls"
+ "description": "Cached Repeated Calls"
},
"string-concat-loop": {
"type": "boolean",
"default": true,
- "description": "Enable detection of String Concatenation in Loop"
+ "description": "String Concatenation in Loop"
},
"no-self-use": {
"type": "boolean",
"default": true,
- "description": "Enable detection of No Self Use"
+ "description": "No Self Use"
},
"use-a-generator": {
"type": "boolean",
"default": true,
- "description": "Enable detection of Use a Generator"
+ "description": "Use a Generator"
}
}
}
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 1c50d38..1ee7a13 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -98,7 +98,6 @@ export async function refactorSmell(
}
const refactorResult = (await response.json()) as RefactorOutput;
- console.log(`Eco: Refactoring successful for smell "${smell.symbol}"`);
return refactorResult;
} catch (error) {
console.error('Eco: Unexpected error in refactorSmell:', error);
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 5aea710..2ba7f0d 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -29,7 +29,7 @@ async function refactorLine(
return refactorResult;
} catch (error) {
console.error('Error refactoring smell:', error);
- vscode.window.showErrorMessage(`Eco: Error refactoring smell: ${error}`);
+ vscode.window.showErrorMessage((error as Error).message);
return;
}
}
@@ -94,9 +94,11 @@ export async function refactorSelectedSmell(
async (progress, token) => {
const result = await refactorLine(smellToRefactor, filePath, contextManager);
- vscode.window.showInformationMessage(
- 'Refactoring report available in sidebar.'
- );
+ if (result && result.refactoredData) {
+ vscode.window.showInformationMessage(
+ 'Refactoring report available in sidebar.'
+ );
+ }
return result;
}
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index 60b146e..63fe437 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -14,78 +14,59 @@ function getLogDirectory(): string {
return path.join(userHome, '.ecooptimizer', 'outputs', 'logs');
}
+const LOG_DIR = getLogDirectory();
+
/**
* Defines log file paths dynamically based on the home directory.
*/
-function getLogFiles(): Record {
- const LOG_DIR = getLogDirectory();
- return {
- 'Main Log': path.join(LOG_DIR, 'main.log'),
- 'Detect Smells Log': path.join(LOG_DIR, 'detect_smells.log'),
- 'Refactor Smell Log': path.join(LOG_DIR, 'refactor_smell.log')
- };
-}
+const LOG_FILES = {
+ main: path.join(LOG_DIR, 'main.log'),
+ detect: path.join(LOG_DIR, 'detect_smells.log'),
+ refactor: path.join(LOG_DIR, 'refactor_smell.log')
+};
// β
Create an output channel for logs
-let outputChannel = vscode.window.createOutputChannel('Eco Optimizer Logs');
-
-/**
- * Registers the command to show logs in VS Code.
- */
-export function showLogsCommand(context: vscode.ExtensionContext) {
- context.subscriptions.push(
- vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.showLogs',
- async () => {
- const LOG_FILES = getLogFiles();
- if (!LOG_FILES['Main Log']) {
- vscode.window.showErrorMessage('Eco: Log directory is not set.');
- return;
- }
-
- const selectedLog: string | undefined = await vscode.window.showQuickPick(
- Object.keys(LOG_FILES),
- {
- placeHolder: 'Select a log file to view'
- }
- );
-
- if (selectedLog) {
- showLogFile(LOG_FILES[selectedLog], selectedLog);
- }
- }
- )
- );
-}
-
-/**
- * Displays the log file content in VS Code's Output Panel.
- */
-function showLogFile(filePath: string, logName: string) {
- outputChannel.clear();
- outputChannel.show();
- outputChannel.appendLine(`π Viewing: ${logName}`);
-
- if (!fs.existsSync(filePath)) {
- outputChannel.appendLine('β οΈ Log file does not exist.');
- return;
+const outputChannels = {
+ main: {
+ channel: vscode.window.createOutputChannel('EcoOptimizer Main'),
+ filePath: LOG_FILES.main
+ },
+ detect: {
+ channel: vscode.window.createOutputChannel('EcoOptimizer Detect'),
+ filePath: LOG_FILES.detect
+ },
+ refactor: {
+ channel: vscode.window.createOutputChannel('EcoOptimizer Refactor'),
+ filePath: LOG_FILES.refactor
}
+};
- fs.readFile(filePath, 'utf8', (err, data) => {
- if (!err) {
- outputChannel.append(data);
+export function startLogging() {
+ Object.entries(outputChannels).forEach(([key, value]) => {
+ value.channel.clear();
+ value.channel.show();
+
+ if (!fs.existsSync(value.filePath)) {
+ value.channel.appendLine('β οΈ Log file does not exist.');
+ return;
}
- });
- // β
Watch the log file for live updates
- fs.watchFile(filePath, { interval: 1000 }, () => {
- fs.readFile(filePath, 'utf8', (err, data) => {
+ fs.readFile(value.filePath, 'utf8', (err, data) => {
if (!err) {
- outputChannel.clear();
- outputChannel.appendLine(`π Viewing: ${logName}`);
- outputChannel.append(data);
+ value.channel.append(data);
}
});
+
+ // β
Watch the log file for live updates
+ fs.watchFile(value.filePath, { interval: 1000 }, () => {
+ fs.readFile(value.filePath, 'utf8', (err, data) => {
+ if (!err) {
+ value.channel.clear();
+ value.channel.appendLine(`π Viewing: ${key}`);
+ value.channel.append(data);
+ }
+ });
+ });
});
}
@@ -93,5 +74,5 @@ function showLogFile(filePath: string, logName: string) {
* Stops watching log files when the extension is deactivated.
*/
export function stopWatchingLogs() {
- Object.values(getLogFiles()).forEach((filePath) => fs.unwatchFile(filePath));
+ Object.values(LOG_FILES).forEach((filePath) => fs.unwatchFile(filePath));
}
diff --git a/src/extension.ts b/src/extension.ts
index abfae69..3e3e973 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -7,7 +7,7 @@ import {
refactorAllSmellsOfType
} from './commands/refactorSmell';
import { wipeWorkCache } from './commands/wipeWorkCache';
-import { showLogsCommand, stopWatchingLogs } from './commands/showLogs';
+import { startLogging, stopWatchingLogs } from './commands/showLogs';
import { ContextManager } from './context/contextManager';
import {
getEnabledSmells,
@@ -92,8 +92,17 @@ export function activate(context: vscode.ExtensionContext) {
)
);
- // Log Viewing Command
- showLogsCommand(context);
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer-vs-code-plugin.startLogging',
+ async () => {
+ console.log('Logging started');
+ startLogging();
+ }
+ )
+ );
+
+ vscode.commands.executeCommand('ecooptimizer-vs-code-plugin.startLogging');
// ===============================================================
// REGISTER VIEWS
From 94f1d7d3cb1a0c9740db6b13d382d51f8def52cd Mon Sep 17 00:00:00 2001
From: tbrar06
Date: Mon, 10 Feb 2025 07:00:12 -0500
Subject: [PATCH 38/83] ssm-lab/capstone--source-code-optimizer#346 removed
unit test path, updated settings name
---
package.json | 9 ++-------
src/extension.ts | 2 +-
src/utils/configManager.ts | 11 -----------
3 files changed, 3 insertions(+), 19 deletions(-)
diff --git a/package.json b/package.json
index 1faeb1a..2c9a7b8 100644
--- a/package.json
+++ b/package.json
@@ -58,17 +58,12 @@
"configuration": {
"title": "EcoOptimizer",
"properties": {
- "ecooptimizer-vs-code-plugin.projectWorkspacePath": {
+ "ecooptimizer.projectWorkspacePath": {
"type": "string",
"default": "",
"description": "Path to the folder to be targeted, relative to current workspace. Defaults to the currently open folder in VS Code."
},
- "ecooptimizer-vs-code-plugin.unitTestCommand": {
- "type": "string",
- "default": "",
- "description": "Command to run unit tests. Should be the same as if written in the terminal (e.i. pytest tests/)."
- },
- "ecooptimizer-vs-code-plugin.logsOutputPath": {
+ "ecooptimizer.logsOutputPath": {
"type": "string",
"default": "",
"description": "Path to store log files and output reports. Defaults to a 'logs' folder inside the workspace."
diff --git a/src/extension.ts b/src/extension.ts
index 3e3e973..46959cf 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -210,7 +210,7 @@ function showSettingsPopup() {
// Open the settings page filtered to extension's settings
vscode.commands.executeCommand(
'workbench.action.openSettings',
- 'ecooptimizer-vs-code-plugin'
+ 'ecooptimizer'
);
} else if (selection === 'Skip for now') {
// Inform user they can configure later
diff --git a/src/utils/configManager.ts b/src/utils/configManager.ts
index 1298152..fc33ffa 100644
--- a/src/utils/configManager.ts
+++ b/src/utils/configManager.ts
@@ -21,16 +21,6 @@ export class ConfigManager {
return resolvedPath;
}
- // get unit test path
- static getUnitTestPath(): string {
- const rawPath = vscode.workspace
- .getConfiguration('ecooptimizer-vs-code-plugin')
- .get('unitTestPath', '');
- const resolvedPath = this.resolvePath(rawPath);
-
- return resolvedPath;
- }
-
// get logs output path
static getLogsOutputPath(): string {
const rawPath = vscode.workspace
@@ -52,7 +42,6 @@ export class ConfigManager {
event.affectsConfiguration(
'ecooptimizer-vs-code-plugin.projectWorkspacePath'
) ||
- event.affectsConfiguration('ecooptimizer-vs-code-plugin.unitTestCommand') ||
event.affectsConfiguration('ecooptimizer-vs-code-plugin.logsOutputPath')
) {
callback();
From 479f92bdfe53009077d14f853dd2acd77bc7d1b4 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 17 Feb 2025 10:45:33 -0500
Subject: [PATCH 39/83] Updated logging to use websockets and store in vscode
built-in log dir
fixes ssm-lab/capstone--source-code-optimizer#393
---
package-lock.json | 69 ++++++++++++++++++++++++-
package.json | 9 ++--
src/api/backend.ts | 30 +++++++++++
src/commands/showLogs.ts | 108 ++++++++++++++++++---------------------
src/extension.ts | 36 +++++--------
5 files changed, 168 insertions(+), 84 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 11d99d8..525d18f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,13 +9,17 @@
"version": "0.0.1",
"dependencies": {
"@types/dotenv": "^6.1.1",
+ "bufferutil": "^4.0.9",
"dotenv": "^16.4.7",
- "dotenv-webpack": "^8.1.0"
+ "dotenv-webpack": "^8.1.0",
+ "utf-8-validate": "^6.0.5",
+ "ws": "^8.18.0"
},
"devDependencies": {
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.92.0",
+ "@types/ws": "^8.5.14",
"@typescript-eslint/eslint-plugin": "^8.17.0",
"@typescript-eslint/parser": "^8.17.0",
"@vscode/test-cli": "^0.0.10",
@@ -484,6 +488,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/ws": {
+ "version": "8.5.14",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz",
+ "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz",
@@ -1240,6 +1253,18 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT"
},
+ "node_modules/bufferutil": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
+ "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "node-gyp-build": "^4.3.0"
+ },
+ "engines": {
+ "node": ">=6.14.2"
+ }
+ },
"node_modules/c8": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
@@ -3188,6 +3213,16 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"license": "MIT"
},
+ "node_modules/node-gyp-build": {
+ "version": "4.8.4",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
+ "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
+ "bin": {
+ "node-gyp-build": "bin.js",
+ "node-gyp-build-optional": "optional.js",
+ "node-gyp-build-test": "build-test.js"
+ }
+ },
"node_modules/node-releases": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
@@ -4506,6 +4541,18 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/utf-8-validate": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.5.tgz",
+ "integrity": "sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "node-gyp-build": "^4.3.0"
+ },
+ "engines": {
+ "node": ">=6.14.2"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -4831,6 +4878,26 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/ws": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/package.json b/package.json
index 2c9a7b8..6ac05b0 100644
--- a/package.json
+++ b/package.json
@@ -33,8 +33,7 @@
{
"command": "ecooptimizer-vs-code-plugin.showRefactorSidebar",
"title": "Show Refactor Sidebar",
- "category": "Eco",
- "enablement": "false"
+ "category": "Eco"
},
{
"command": "ecooptimizer-vs-code-plugin.pauseRefactorSidebar",
@@ -174,6 +173,7 @@
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.92.0",
+ "@types/ws": "^8.5.14",
"@typescript-eslint/eslint-plugin": "^8.17.0",
"@typescript-eslint/parser": "^8.17.0",
"@vscode/test-cli": "^0.0.10",
@@ -188,7 +188,10 @@
},
"dependencies": {
"@types/dotenv": "^6.1.1",
+ "bufferutil": "^4.0.9",
"dotenv": "^16.4.7",
- "dotenv-webpack": "^8.1.0"
+ "dotenv-webpack": "^8.1.0",
+ "utf-8-validate": "^6.0.5",
+ "ws": "^8.18.0"
}
}
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 1ee7a13..7cf0d76 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -4,6 +4,36 @@ import { Smell } from '../types';
const BASE_URL = 'http://127.0.0.1:8000'; // API URL for Python backend
+export async function initLogs(log_dir: string) {
+ const url = `${BASE_URL}/logs/init`;
+
+ try {
+ console.log('Initializing and synching logs with backend');
+
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ log_dir })
+ });
+
+ if (!response.ok) {
+ console.error(`Unable to initialize logging: ${JSON.stringify(response)}`);
+
+ return false;
+ }
+
+ return true;
+ } catch (error: any) {
+ console.error(`Eco: Unable to initialize logging: ${error.message}`);
+ vscode.window.showErrorMessage(
+ 'Eco: Unable to reach the backend. Please check your connection.'
+ );
+ return false;
+ }
+}
+
// β
Fetch detected smells for a given file (only enabled smells)
export async function fetchSmells(
filePath: string,
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index 63fe437..d639d83 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -1,72 +1,62 @@
import * as vscode from 'vscode';
-import * as fs from 'fs';
-import * as path from 'path';
+import WebSocket from 'ws';
-/**
- * Returns the EcoOptimizer log directory inside the user's home directory.
- */
-function getLogDirectory(): string {
- const userHome = process.env.HOME || process.env.USERPROFILE;
- if (!userHome) {
- vscode.window.showErrorMessage('Eco: Unable to determine user home directory.');
- return '';
+import { initLogs } from '../api/backend';
+
+const WEBSOCKET_BASE_URL = 'ws://127.0.0.1:8000/logs';
+
+let mainLogChannel: vscode.OutputChannel | undefined;
+let detectSmellsChannel: vscode.OutputChannel | undefined;
+let refactorSmellChannel: vscode.OutputChannel | undefined;
+
+export async function startLogging(context: vscode.ExtensionContext) {
+ const initialized = await initializeBackendSync(context);
+
+ if (initialized) {
+ startWebSocket('main', 'EcoOptimizer: Main Logs');
+ startWebSocket('detect', 'EcoOptimizer: Detect Smells');
+ startWebSocket('refactor', 'EcoOptimizer: Refactor Smell');
+
+ console.log('Successfully initialized logging.');
}
- return path.join(userHome, '.ecooptimizer', 'outputs', 'logs');
}
-const LOG_DIR = getLogDirectory();
+async function initializeBackendSync(context: vscode.ExtensionContext) {
+ return await initLogs(context.logUri.fsPath);
+}
-/**
- * Defines log file paths dynamically based on the home directory.
- */
-const LOG_FILES = {
- main: path.join(LOG_DIR, 'main.log'),
- detect: path.join(LOG_DIR, 'detect_smells.log'),
- refactor: path.join(LOG_DIR, 'refactor_smell.log')
-};
+function startWebSocket(logType: string, channelName: string) {
+ const url = `${WEBSOCKET_BASE_URL}/${logType}`;
+ const ws = new WebSocket(url);
-// β
Create an output channel for logs
-const outputChannels = {
- main: {
- channel: vscode.window.createOutputChannel('EcoOptimizer Main'),
- filePath: LOG_FILES.main
- },
- detect: {
- channel: vscode.window.createOutputChannel('EcoOptimizer Detect'),
- filePath: LOG_FILES.detect
- },
- refactor: {
- channel: vscode.window.createOutputChannel('EcoOptimizer Refactor'),
- filePath: LOG_FILES.refactor
+ let channel: vscode.OutputChannel;
+ if (logType === 'main') {
+ mainLogChannel = vscode.window.createOutputChannel(channelName);
+ channel = mainLogChannel;
+ } else if (logType === 'detect') {
+ detectSmellsChannel = vscode.window.createOutputChannel(channelName);
+ channel = detectSmellsChannel;
+ } else if (logType === 'refactor') {
+ refactorSmellChannel = vscode.window.createOutputChannel(channelName);
+ channel = refactorSmellChannel;
+ } else {
+ return;
}
-};
-export function startLogging() {
- Object.entries(outputChannels).forEach(([key, value]) => {
- value.channel.clear();
- value.channel.show();
+ ws.on('message', function message(data) {
+ channel.append(data.toString('utf8'));
+ });
- if (!fs.existsSync(value.filePath)) {
- value.channel.appendLine('β οΈ Log file does not exist.');
- return;
- }
+ ws.onerror = (event) => {
+ channel.appendLine(`WebSocket error: ${event}`);
+ };
- fs.readFile(value.filePath, 'utf8', (err, data) => {
- if (!err) {
- value.channel.append(data);
- }
- });
+ ws.on('close', function close() {
+ channel.appendLine(`WebSocket connection closed for ${logType}`);
+ });
- // β
Watch the log file for live updates
- fs.watchFile(value.filePath, { interval: 1000 }, () => {
- fs.readFile(value.filePath, 'utf8', (err, data) => {
- if (!err) {
- value.channel.clear();
- value.channel.appendLine(`π Viewing: ${key}`);
- value.channel.append(data);
- }
- });
- });
+ ws.on('open', function open() {
+ channel.appendLine(`Connected to ${channelName} via WebSocket`);
});
}
@@ -74,5 +64,7 @@ export function startLogging() {
* Stops watching log files when the extension is deactivated.
*/
export function stopWatchingLogs() {
- Object.values(LOG_FILES).forEach((filePath) => fs.unwatchFile(filePath));
+ mainLogChannel?.dispose();
+ detectSmellsChannel?.dispose();
+ refactorSmellChannel?.dispose();
}
diff --git a/src/extension.ts b/src/extension.ts
index 46959cf..4289f0e 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -20,17 +20,12 @@ import { LineSelectionManager } from './ui/lineSelectionManager';
export function activate(context: vscode.ExtensionContext) {
console.log('Eco: Refactor Plugin Activated Successfully');
+ const contextManager = new ContextManager(context);
// Show the settings popup if needed
showSettingsPopup();
- // Register a listener for configuration changes
- context.subscriptions.push(
- vscode.workspace.onDidChangeConfiguration((event) => {
- handleConfigurationChange(event);
- })
- );
- const contextManager = new ContextManager(context);
+ startLogging(context);
let smellsData = contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!) || {};
contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, smellsData);
@@ -92,18 +87,6 @@ export function activate(context: vscode.ExtensionContext) {
)
);
- context.subscriptions.push(
- vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.startLogging',
- async () => {
- console.log('Logging started');
- startLogging();
- }
- )
- );
-
- vscode.commands.executeCommand('ecooptimizer-vs-code-plugin.startLogging');
-
// ===============================================================
// REGISTER VIEWS
// ===============================================================
@@ -141,6 +124,13 @@ export function activate(context: vscode.ExtensionContext) {
// ADD LISTENERS
// ===============================================================
+ // Register a listener for configuration changes
+ context.subscriptions.push(
+ vscode.workspace.onDidChangeConfiguration((event) => {
+ handleConfigurationChange(event);
+ })
+ );
+
vscode.window.onDidChangeVisibleTextEditors(async (editors) => {
handleEditorChanges(contextManager, editors);
});
@@ -157,6 +147,7 @@ export function activate(context: vscode.ExtensionContext) {
// Updates directory of file states (for checking if modified)
context.subscriptions.push(
vscode.workspace.onDidSaveTextDocument(async (document) => {
+ console.log('Eco: Detected document saved event');
await updateHash(contextManager, document);
})
);
@@ -170,9 +161,10 @@ export function activate(context: vscode.ExtensionContext) {
// Initializes first state of document when opened while extension is active
context.subscriptions.push(
- vscode.workspace.onDidOpenTextDocument(
- async (document) => await updateHash(contextManager, document)
- )
+ vscode.workspace.onDidOpenTextDocument(async (document) => {
+ console.log('Eco: Detected document opened event');
+ await updateHash(contextManager, document);
+ })
);
// ===============================================================
From 59d718c8c8fa4b8eee963d0b43cb4558ca7cddc3 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Tue, 18 Feb 2025 16:19:36 -0500
Subject: [PATCH 40/83] minor fixes and adjustments
---
.env | 3 +++
src/api/backend.ts | 3 ++-
src/commands/detectSmells.ts | 5 -----
src/commands/refactorSmell.ts | 34 +++++++++++++++++++++-------------
src/commands/showLogs.ts | 3 ++-
src/commands/wipeWorkCache.ts | 14 ++++++++------
src/utils/editorUtils.ts | 19 -------------------
src/utils/envConfig.ts | 8 +++++++-
src/utils/hashDocs.ts | 3 +--
9 files changed, 44 insertions(+), 48 deletions(-)
diff --git a/.env b/.env
index a3e7ba0..55f1a49 100644
--- a/.env
+++ b/.env
@@ -1,3 +1,6 @@
+SERVER_URL='http://127.0.0.1:8000'
SMELL_MAP_KEY='workspaceSmells'
FILE_CHANGES_KEY='lastSavedHashes'
LAST_USED_SMELLS_KEY='lastUsedSmells'
+CURRENT_REFACTOR_DATA_KEY='refactorData'
+ACTIVE_DIFF_KEY='activeDiff'
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 7cf0d76..80c4352 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,8 +1,9 @@
import * as vscode from 'vscode';
import { Smell } from '../types';
+import { envConfig } from '../utils/envConfig';
-const BASE_URL = 'http://127.0.0.1:8000'; // API URL for Python backend
+const BASE_URL = envConfig.SERVER_URL; // API URL for Python backend
export async function initLogs(log_dir: string) {
const url = `${BASE_URL}/logs/init`;
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 4d61186..06ccf87 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -106,7 +106,6 @@ export async function detectSmells(contextManager: ContextManager) {
} else {
if (fileSmells) {
console.log(`Eco: File changed. Updating smells.`);
- await wipeWorkCache(contextManager, 'fileChange');
} else {
console.log(`Eco: No cached smells found. Fetching from backend.`);
}
@@ -120,7 +119,6 @@ export async function detectSmells(contextManager: ContextManager) {
return;
}
- // β
Highlight smells in editor
console.log(`Eco: Highlighting detected smells in ${filePath}.`);
if (!fileHighlighter) {
fileHighlighter = new FileHighlighter(contextManager);
@@ -132,9 +130,6 @@ export async function detectSmells(contextManager: ContextManager) {
);
}
-/**
- * β
Fetches the currently enabled smells from VS Code settings.
- */
function getEnabledSmells(): { [key: string]: boolean } {
return vscode.workspace.getConfiguration('ecooptimizer').get('enableSmells', {});
}
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 2ba7f0d..c125555 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -40,7 +40,9 @@ export async function refactorSelectedSmell(
) {
const { editor, filePath } = getEditorAndFilePath();
- const pastData = contextManager.getWorkspaceData('refactorData');
+ const pastData = contextManager.getWorkspaceData(
+ envConfig.CURRENT_REFACTOR_DATA_KEY!
+ );
// Clean up temp directory if not removed
if (pastData) {
@@ -113,7 +115,7 @@ export async function refactorSelectedSmell(
const { refactoredData } = refactorResult;
- startRefactoringSession(contextManager, editor, refactoredData);
+ await startRefactoringSession(contextManager, editor, refactoredData);
if (refactorResult.updatedSmells.length) {
const fileHighlighter = new FileHighlighter(contextManager);
@@ -131,7 +133,9 @@ export async function refactorAllSmellsOfType(
) {
const { editor, filePath } = getEditorAndFilePath();
- const pastData = contextManager.getWorkspaceData('refactorData');
+ const pastData = contextManager.getWorkspaceData(
+ envConfig.CURRENT_REFACTOR_DATA_KEY!
+ );
// Clean up temp directory if not removed
if (pastData) {
@@ -264,10 +268,13 @@ async function startRefactoringSession(
editor: vscode.TextEditor,
refactoredData: RefactoredData | MultiRefactoredData
) {
- await vscode.commands.executeCommand('extension.refactorSidebar.focus');
-
// Store only the diff editor state
- await contextManager.setWorkspaceData('refactorData', refactoredData);
+ await contextManager.setWorkspaceData(
+ envConfig.CURRENT_REFACTOR_DATA_KEY!,
+ refactoredData
+ );
+
+ await vscode.commands.executeCommand('extension.refactorSidebar.focus');
//Read the refactored code
const refactoredCode = vscode.Uri.file(refactoredData.targetFile.refactored);
@@ -285,7 +292,7 @@ async function startRefactoringSession(
};
});
- await contextManager.setWorkspaceData('activeDiff', {
+ await contextManager.setWorkspaceData(envConfig.ACTIVE_DIFF_KEY!, {
files: allFiles,
firstOpen: true,
isOpen: true
@@ -308,15 +315,16 @@ async function startRefactoringSession(
sidebarState.isOpening = false;
}
-function cleanTemps(pastData: any) {
+async function cleanTemps(pastData: any) {
console.log('Cleaning up stale artifacts');
- const tempDirs = pastData!.tempDir! || pastData!.tempDirs!;
+ const tempDirs =
+ (pastData!.tempDir! as string) || (pastData!.tempDirs! as string[]);
if (Array.isArray(tempDirs)) {
- tempDirs.forEach((dir) => {
- fs.promises.rm(dir, { recursive: true });
- });
+ for (const dir in tempDirs) {
+ await fs.promises.rm(dir, { recursive: true, force: true });
+ }
} else {
- fs.promises.rm(tempDirs!, { recursive: true });
+ await fs.promises.rm(tempDirs, { recursive: true, force: true });
}
}
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index d639d83..49d64c8 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -2,8 +2,9 @@ import * as vscode from 'vscode';
import WebSocket from 'ws';
import { initLogs } from '../api/backend';
+import { envConfig } from '../utils/envConfig';
-const WEBSOCKET_BASE_URL = 'ws://127.0.0.1:8000/logs';
+const WEBSOCKET_BASE_URL = `ws://${envConfig.SERVER_URL}/logs`;
let mainLogChannel: vscode.OutputChannel | undefined;
let detectSmellsChannel: vscode.OutputChannel | undefined;
diff --git a/src/commands/wipeWorkCache.ts b/src/commands/wipeWorkCache.ts
index dec59db..9c620e7 100644
--- a/src/commands/wipeWorkCache.ts
+++ b/src/commands/wipeWorkCache.ts
@@ -13,16 +13,20 @@ export async function wipeWorkCache(
// β
Clear stored smells cache
await contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, {});
+ if (reason === 'manual') {
+ await contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, {});
+ }
+
// β
Update file hashes for all open editors
- const openEditors = vscode.window.visibleTextEditors;
+ const visibleEditors = vscode.window.visibleTextEditors;
- if (openEditors.length === 0) {
+ if (visibleEditors.length === 0) {
console.log('Eco: No open files to update hash.');
} else {
- console.log(`Eco: Updating cache for ${openEditors.length} open files.`);
+ console.log(`Eco: Updating cache for ${visibleEditors.length} visible files.`);
}
- for (const editor of openEditors) {
+ for (const editor of visibleEditors) {
if (editor.document) {
await updateHash(contextManager, editor.document);
}
@@ -33,8 +37,6 @@ export async function wipeWorkCache(
if (reason === 'settings') {
message =
'Eco: Smell detection settings changed. Cache wiped to apply updates. β
';
- } else if (reason === 'fileChange') {
- message = 'Eco: File changed. Cache wiped to refresh smell detection. π';
} else if (reason === 'manual') {
message = 'Eco: Workspace cache manually wiped by user. β
';
}
diff --git a/src/utils/editorUtils.ts b/src/utils/editorUtils.ts
index 162b760..e15d1ca 100644
--- a/src/utils/editorUtils.ts
+++ b/src/utils/editorUtils.ts
@@ -13,25 +13,6 @@ export function getEditorAndFilePath(): {
const filePath = activeEditor?.document.uri.fsPath;
return { editor: activeEditor, filePath };
}
-// /**
-// * Gets the active editor, its file path and the workspace that contains it if an editor is open.
-// * @returns {{ editor: vscode.TextEditor | undefined, filePath: string | undefined, workspace: string | undefined }}
-// * An object containing the active editor, the file path and workspace, or undefined for all three if no editor is open.
-// */
-// export function getEditorAndPaths(): {
-// editor: vscode.TextEditor | undefined;
-// filePath: string | undefined;
-// workspace: string | undefined;
-// } {
-// const activeEditor = vscode.window.activeTextEditor;
-// const fileUri = activeEditor?.document.uri!;
-// const workspace = vscode.workspace.getWorkspaceFolder(fileUri)?.uri.fsPath;
-// return {
-// editor: activeEditor,
-// filePath: fileUri.fsPath,
-// workspace: workspace
-// };
-// }
/**
* Gets the active editor if an editor is open.
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts
index bc35265..3358bbc 100644
--- a/src/utils/envConfig.ts
+++ b/src/utils/envConfig.ts
@@ -3,13 +3,19 @@ import * as dotenv from 'dotenv';
dotenv.config();
export interface EnvConfig {
+ SERVER_URL?: string;
SMELL_MAP_KEY?: string;
FILE_CHANGES_KEY?: string;
LAST_USED_SMELLS_KEY?: string;
+ CURRENT_REFACTOR_DATA_KEY?: string;
+ ACTIVE_DIFF_KEY?: string;
}
export const envConfig: EnvConfig = {
+ SERVER_URL: process.env.SERVER_URL,
SMELL_MAP_KEY: process.env.SMELL_MAP_KEY,
FILE_CHANGES_KEY: process.env.FILE_CHANGES_KEY,
- LAST_USED_SMELLS_KEY: process.env.LAST_USED_SMELLS_KEY
+ LAST_USED_SMELLS_KEY: process.env.LAST_USED_SMELLS_KEY,
+ CURRENT_REFACTOR_DATA_KEY: process.env.CURRENT_REFACTOR_DATA_KEY,
+ ACTIVE_DIFF_KEY: process.env.ACTIVE_DIFF_KEY
};
diff --git a/src/utils/hashDocs.ts b/src/utils/hashDocs.ts
index a4a982d..04fa249 100644
--- a/src/utils/hashDocs.ts
+++ b/src/utils/hashDocs.ts
@@ -13,7 +13,6 @@ export async function updateHash(
contextManager: ContextManager,
document: vscode.TextDocument
) {
- console.log(`Updating hash for ${document.fileName}`);
const lastSavedHashes = contextManager.getWorkspaceData(
envConfig.FILE_CHANGES_KEY!,
{}
@@ -22,7 +21,7 @@ export async function updateHash(
const currentHash = hashContent(document.getText());
if (!lastHash || lastHash !== currentHash) {
- console.log(`Document ${document.uri.fsPath} has changed since last save.`);
+ console.log(`Document ${document.fileName} has changed since last save.`);
lastSavedHashes[document.fileName] = currentHash;
await contextManager.setWorkspaceData(
envConfig.FILE_CHANGES_KEY!,
From 93f2788e5331ad97e315c4da8e526daa454ef85e Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Tue, 18 Feb 2025 16:59:17 -0500
Subject: [PATCH 41/83] fixed server url env variable + added settings popup
feature
---
.env | 2 +-
src/api/backend.ts | 2 +-
src/extension.ts | 22 ++++++++++++++++++----
src/utils/envConfig.ts | 2 +-
4 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/.env b/.env
index 55f1a49..86798f4 100644
--- a/.env
+++ b/.env
@@ -1,4 +1,4 @@
-SERVER_URL='http://127.0.0.1:8000'
+SERVER_URL='127.0.0.1:8000'
SMELL_MAP_KEY='workspaceSmells'
FILE_CHANGES_KEY='lastSavedHashes'
LAST_USED_SMELLS_KEY='lastUsedSmells'
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 80c4352..c65f634 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -3,7 +3,7 @@ import * as vscode from 'vscode';
import { Smell } from '../types';
import { envConfig } from '../utils/envConfig';
-const BASE_URL = envConfig.SERVER_URL; // API URL for Python backend
+const BASE_URL = `http://${envConfig.SERVER_URL}`; // API URL for Python backend
export async function initLogs(log_dir: string) {
const url = `${BASE_URL}/logs/init`;
diff --git a/src/extension.ts b/src/extension.ts
index 4289f0e..3efe8f7 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -23,7 +23,15 @@ export function activate(context: vscode.ExtensionContext) {
const contextManager = new ContextManager(context);
// Show the settings popup if needed
- showSettingsPopup();
+ // TODO: Setting to re-enable popup if disabled
+ const settingsPopupChoice =
+ contextManager.getGlobalData('showSettingsPopup');
+
+ if (settingsPopupChoice === undefined || settingsPopupChoice) {
+ showSettingsPopup(contextManager);
+ }
+
+ console.log('environment variables:', envConfig);
startLogging(context);
@@ -181,7 +189,7 @@ export function activate(context: vscode.ExtensionContext) {
});
}
-function showSettingsPopup() {
+function showSettingsPopup(contextManager: ContextManager) {
// Check if the required settings are already configured
const config = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin');
const workspacePath = config.get('projectWorkspacePath', '');
@@ -195,7 +203,8 @@ function showSettingsPopup() {
'Please configure the paths for your workspace and logs.',
{ modal: true },
'Continue', // Button to open settings
- 'Skip for now' // Button to dismiss
+ 'Skip', // Button to dismiss
+ 'Never show this again'
)
.then((selection) => {
if (selection === 'Continue') {
@@ -204,11 +213,16 @@ function showSettingsPopup() {
'workbench.action.openSettings',
'ecooptimizer'
);
- } else if (selection === 'Skip for now') {
+ } else if (selection === 'Skip') {
// Inform user they can configure later
vscode.window.showInformationMessage(
'You can configure the paths later in the settings.'
);
+ } else if (selection === 'Never show this again') {
+ contextManager.setGlobalData('showSettingsPopup', false);
+ vscode.window.showInformationMessage(
+ 'You can re-enable this popup again in the settings.'
+ );
}
});
}
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts
index 3358bbc..f60ba77 100644
--- a/src/utils/envConfig.ts
+++ b/src/utils/envConfig.ts
@@ -2,7 +2,7 @@ import * as dotenv from 'dotenv';
dotenv.config();
-export interface EnvConfig {
+interface EnvConfig {
SERVER_URL?: string;
SMELL_MAP_KEY?: string;
FILE_CHANGES_KEY?: string;
From c37e68266658691edeb30778ece33ae4420908ce Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Tue, 18 Feb 2025 18:40:47 -0500
Subject: [PATCH 42/83] fix: explicitly close websockets on extension
disconnect
fixes ssm-lab/capstone--source-code-optimizer#393
---
src/commands/showLogs.ts | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index 49d64c8..b2d7e9d 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -6,6 +6,8 @@ import { envConfig } from '../utils/envConfig';
const WEBSOCKET_BASE_URL = `ws://${envConfig.SERVER_URL}/logs`;
+let websockets: WebSocket[] = [];
+
let mainLogChannel: vscode.OutputChannel | undefined;
let detectSmellsChannel: vscode.OutputChannel | undefined;
let refactorSmellChannel: vscode.OutputChannel | undefined;
@@ -23,12 +25,17 @@ export async function startLogging(context: vscode.ExtensionContext) {
}
async function initializeBackendSync(context: vscode.ExtensionContext) {
- return await initLogs(context.logUri.fsPath);
+ const logPath = context.logUri.fsPath;
+
+ console.log('Log path:', logPath);
+
+ return await initLogs(logPath);
}
function startWebSocket(logType: string, channelName: string) {
const url = `${WEBSOCKET_BASE_URL}/${logType}`;
const ws = new WebSocket(url);
+ websockets.push(ws);
let channel: vscode.OutputChannel;
if (logType === 'main') {
@@ -65,6 +72,8 @@ function startWebSocket(logType: string, channelName: string) {
* Stops watching log files when the extension is deactivated.
*/
export function stopWatchingLogs() {
+ websockets.forEach((ws) => ws.close());
+
mainLogChannel?.dispose();
detectSmellsChannel?.dispose();
refactorSmellChannel?.dispose();
From 23fcbea2ab4140dfc87cbc2e91fc18ef0f6e39f6 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Tue, 18 Feb 2025 18:44:44 -0500
Subject: [PATCH 43/83] fixed async file handling
---
src/ui/refactorView.ts | 107 ++++++++++++++++++++++++-----------------
1 file changed, 64 insertions(+), 43 deletions(-)
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
index eb9c234..a1f2b34 100644
--- a/src/ui/refactorView.ts
+++ b/src/ui/refactorView.ts
@@ -1,8 +1,10 @@
import * as vscode from 'vscode';
import path from 'path';
+import * as fs from 'fs';
+
+import { envConfig } from '../utils/envConfig';
import { readFileSync } from 'fs';
import { ActiveDiff } from '../types';
-import * as fs from 'fs';
import { sidebarState } from '../utils/handleEditorChange';
import { MultiRefactoredData } from '../commands/refactorSmell';
@@ -31,8 +33,9 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
console.log('Webview is visible');
if (webviewView.visible) {
// Use acquireVsCodeApi to get the webview state
- const savedState =
- this._context.workspaceState.get('refactorData');
+ const savedState = this._context.workspaceState.get(
+ envConfig.CURRENT_REFACTOR_DATA_KEY!
+ );
if (savedState) {
this.updateView();
@@ -59,11 +62,11 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
sidebarState.isOpening = false;
break;
case 'accept':
- this.applyRefactoring();
- this.closeViews();
+ await this.applyRefactoring();
+ await this.closeViews();
break;
case 'reject':
- this.closeViews();
+ await this.closeViews();
break;
}
});
@@ -72,8 +75,9 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
async updateView() {
console.log('Updating view');
- const refactoredData =
- this._context.workspaceState.get('refactorData')!;
+ const refactoredData = this._context.workspaceState.get(
+ envConfig.CURRENT_REFACTOR_DATA_KEY!
+ )!;
this._file_map.set(
vscode.Uri.file(refactoredData.targetFile.original),
@@ -90,7 +94,9 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
}
private async openView(refactoredData: RefactoredData) {
- const diffView = this._context.workspaceState.get('activeDiff')!;
+ const diffView = this._context.workspaceState.get(
+ envConfig.ACTIVE_DIFF_KEY!
+ )!;
if (diffView.isOpen) {
console.log('starting view');
@@ -111,9 +117,11 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
this._view!.webview.postMessage({ command: 'pause' });
}
- clearView() {
- this._view?.webview.postMessage({ command: 'clear' });
+ async clearView() {
+ await this._view?.webview.postMessage({ command: 'clear' });
this._file_map = new Map();
+
+ console.log('View cleared');
}
private _getHtml(webview: vscode.Webview): string {
@@ -137,42 +145,55 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
return htmlContent;
}
- private closeViews() {
- console.log('Cleaning up webview');
- this.clearView();
- vscode.commands.executeCommand('workbench.action.closeActiveEditor');
- vscode.commands.executeCommand('workbench.view.explorer');
-
- this._context.workspaceState.update('activeDiff', undefined);
-
- const tempDirs =
- this._context.workspaceState.get('refactorData')?.tempDir! ||
- this._context.workspaceState.get('refactorData')
- ?.tempDirs;
-
- console.log(`temp dir: ${tempDirs}`);
-
- if (Array.isArray(tempDirs)) {
- tempDirs.forEach(async (dir) => {
- fs.rmSync(dir, { recursive: true });
- });
- } else {
- fs.rmSync(tempDirs!, { recursive: true });
+ private async closeViews() {
+ await this.clearView();
+ try {
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+ await vscode.commands.executeCommand('workbench.view.explorer');
+
+ await this._context.workspaceState.update(
+ envConfig.ACTIVE_DIFF_KEY!,
+ undefined
+ );
+
+ const tempDirs =
+ this._context.workspaceState.get(
+ envConfig.CURRENT_REFACTOR_DATA_KEY!
+ )?.tempDir ||
+ this._context.workspaceState.get(
+ envConfig.CURRENT_REFACTOR_DATA_KEY!
+ )?.tempDirs;
+
+ if (Array.isArray(tempDirs)) {
+ for (const dir in tempDirs) {
+ await fs.promises.rm(dir, { recursive: true, force: true });
+ }
+ } else if (tempDirs) {
+ await fs.promises.rm(tempDirs, { recursive: true, force: true });
+ }
+ } catch (err) {
+ console.error('Error closing views', err);
}
- this._context.workspaceState.update('refactorData', undefined);
+ console.log('Closed views');
+
+ await this._context.workspaceState.update(
+ envConfig.CURRENT_REFACTOR_DATA_KEY!,
+ undefined
+ );
}
private async applyRefactoring() {
- this._file_map!.forEach((refactored, original) => {
- vscode.window.showInformationMessage('Applying Eco changes...');
- console.log(`refactored: ${refactored}\noriginal: ${original}`);
- const modifiedContent = fs.readFileSync(refactored.fsPath, {
- encoding: 'utf-8'
- });
-
- fs.writeFileSync(original.fsPath, modifiedContent);
- });
- await vscode.window.showInformationMessage('Refactoring applied successfully!');
+ try {
+ for (const [original, refactored] of this._file_map.entries()) {
+ const content = await vscode.workspace.fs.readFile(refactored);
+ await vscode.workspace.fs.writeFile(original, content);
+ await vscode.workspace.save(original);
+ console.log(`Applied refactoring to ${original.fsPath}`);
+ }
+ vscode.window.showInformationMessage('Refactoring applied successfully!');
+ } catch (error) {
+ console.error('Error applying refactoring:', error);
+ }
}
}
From aa0697b8cf2ec9913b1447e52d290c389f6dc6ca Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Tue, 18 Feb 2025 22:54:41 -0500
Subject: [PATCH 44/83] Implement automatic reconnection to backend server
fixes ssm-lab/capstone--source-code-optimizer#394
---
src/api/backend.ts | 14 ++++++
src/commands/detectSmells.ts | 23 ++++++++-
src/commands/refactorSmell.ts | 8 +++
src/commands/showLogs.ts | 92 ++++++++++++++++++++++++++++++-----
src/extension.ts | 41 +++++++++++-----
src/utils/serverStatus.ts | 29 +++++++++++
6 files changed, 183 insertions(+), 24 deletions(-)
create mode 100644 src/utils/serverStatus.ts
diff --git a/src/api/backend.ts b/src/api/backend.ts
index c65f634..c27d2a4 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -2,9 +2,23 @@ import * as vscode from 'vscode';
import { Smell } from '../types';
import { envConfig } from '../utils/envConfig';
+import { serverStatus } from '../utils/serverStatus';
const BASE_URL = `http://${envConfig.SERVER_URL}`; // API URL for Python backend
+export async function checkServerStatus() {
+ try {
+ const response = await fetch('http://localhost:8000/health');
+ if (response.ok) {
+ serverStatus.setStatus('up');
+ } else {
+ serverStatus.setStatus('down');
+ }
+ } catch {
+ serverStatus.setStatus('down');
+ }
+}
+
export async function initLogs(log_dir: string) {
const url = `${BASE_URL}/logs/init`;
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 06ccf87..030e09f 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -1,4 +1,5 @@
import * as vscode from 'vscode';
+
import { FileHighlighter } from '../ui/fileHighlighter';
import { getEditorAndFilePath } from '../utils/editorUtils';
import { fetchSmells } from '../api/backend';
@@ -7,6 +8,21 @@ import { envConfig } from '../utils/envConfig';
import { hashContent, updateHash } from '../utils/hashDocs';
import { wipeWorkCache } from './wipeWorkCache'; // β
Import cache wipe function
import { Smell } from '../types';
+import { serverStatus } from '../utils/serverStatus';
+
+let serverOn: boolean = true;
+
+serverStatus.on('change', (newStatus) => {
+ console.log('Server status changed:', newStatus);
+ if (newStatus === 'down') {
+ serverOn = false;
+ vscode.window.showWarningMessage(
+ 'Smell detection limited. Only cached smells will be shown.'
+ );
+ } else {
+ serverOn = true;
+ }
+});
export interface SmellDetectRecord {
hash: string;
@@ -103,7 +119,7 @@ export async function detectSmells(contextManager: ContextManager) {
console.log(`Eco: Using cached smells for ${filePath}`);
vscode.window.showInformationMessage(`Eco: Using cached smells for ${filePath}`);
smellsData = fileSmells.smells;
- } else {
+ } else if (serverOn) {
if (fileSmells) {
console.log(`Eco: File changed. Updating smells.`);
} else {
@@ -112,6 +128,11 @@ export async function detectSmells(contextManager: ContextManager) {
updateHash(contextManager, editor.document);
smellsData = await fetchAndStoreSmells();
+ } else {
+ vscode.window.showWarningMessage(
+ 'Action blocked: Server is down and no cached smells exists for this file version.'
+ );
+ return;
}
if (!smellsData || smellsData.length === 0) {
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index c125555..8d8b452 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -11,6 +11,14 @@ import { FileHighlighter } from '../ui/fileHighlighter';
import { ContextManager } from '../context/contextManager';
import { RefactorManager } from '../ui/refactorManager';
import { setTimeout } from 'timers/promises';
+import { serverStatus } from '../utils/serverStatus';
+
+serverStatus.on('change', (newStatus) => {
+ console.log('Server status changed:', newStatus);
+ if (newStatus === 'down') {
+ vscode.window.showWarningMessage('No refactoring is possible at this time.');
+ }
+});
export interface MultiRefactoredData {
tempDirs: string[];
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index b2d7e9d..588492c 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -3,6 +3,22 @@ import WebSocket from 'ws';
import { initLogs } from '../api/backend';
import { envConfig } from '../utils/envConfig';
+import { serverStatus } from '../utils/serverStatus';
+import { globalData } from '../extension';
+
+class LogInitializationError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'LogInitializationError';
+ }
+}
+
+class WebSocketInitializationError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'WebSocketInitializationError';
+ }
+}
const WEBSOCKET_BASE_URL = `ws://${envConfig.SERVER_URL}/logs`;
@@ -12,24 +28,76 @@ let mainLogChannel: vscode.OutputChannel | undefined;
let detectSmellsChannel: vscode.OutputChannel | undefined;
let refactorSmellChannel: vscode.OutputChannel | undefined;
-export async function startLogging(context: vscode.ExtensionContext) {
- const initialized = await initializeBackendSync(context);
+let CHANNELS_CREATED = false;
- if (initialized) {
- startWebSocket('main', 'EcoOptimizer: Main Logs');
- startWebSocket('detect', 'EcoOptimizer: Detect Smells');
- startWebSocket('refactor', 'EcoOptimizer: Refactor Smell');
+serverStatus.on('change', async (newStatus) => {
+ console.log('Server status changed:', newStatus);
+ if (newStatus === 'down') {
+ mainLogChannel?.appendLine('Server connection lost');
+ } else {
+ mainLogChannel?.appendLine('Server connection re-established.');
+ await startLogging();
+ }
+});
+
+export async function startLogging(retries = 3, delay = 1000): Promise {
+ let logInitialized = false;
+ const logPath = globalData.contextManager?.context.logUri?.fsPath;
+
+ if (!logPath) {
+ console.error('Missing contextManager or logUri. Cannot initialize logging.');
+ return;
+ }
- console.log('Successfully initialized logging.');
+ for (let attempt = 1; attempt <= retries; attempt++) {
+ try {
+ if (!logInitialized) {
+ logInitialized = await initLogs(logPath);
+
+ if (!logInitialized) {
+ throw new LogInitializationError(
+ `Failed to initialize logs at path: ${logPath}`
+ );
+ }
+ console.log('Log initialization successful.');
+ }
+
+ if (CHANNELS_CREATED) {
+ console.warn(
+ 'Logging channels already initialized. Skipping WebSocket setup.'
+ );
+ return;
+ }
+
+ // Try initializing WebSockets separately
+ try {
+ initializeWebSockets();
+ console.log('Successfully initialized WebSockets. Logging is now active.');
+ return; // Exit function if everything is successful
+ } catch {
+ throw new WebSocketInitializationError('Failed to initialize WebSockets.');
+ }
+ } catch (error) {
+ const err = error as Error;
+ console.error(`[Attempt ${attempt}/${retries}] ${err.name}: ${err.message}`);
+
+ if (attempt < retries) {
+ console.log(`Retrying in ${delay}ms...`);
+ await new Promise((resolve) => setTimeout(resolve, delay));
+ delay *= 2; // Exponential backoff
+ } else {
+ console.error('Max retries reached. Logging process failed.');
+ }
+ }
}
}
-async function initializeBackendSync(context: vscode.ExtensionContext) {
- const logPath = context.logUri.fsPath;
-
- console.log('Log path:', logPath);
+function initializeWebSockets() {
+ startWebSocket('main', 'EcoOptimizer: Main Logs');
+ startWebSocket('detect', 'EcoOptimizer: Detect Smells');
+ startWebSocket('refactor', 'EcoOptimizer: Refactor Smell');
- return await initLogs(logPath);
+ CHANNELS_CREATED = true;
}
function startWebSocket(logType: string, channelName: string) {
diff --git a/src/extension.ts b/src/extension.ts
index 3efe8f7..8f6912f 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -7,7 +7,7 @@ import {
refactorAllSmellsOfType
} from './commands/refactorSmell';
import { wipeWorkCache } from './commands/wipeWorkCache';
-import { startLogging, stopWatchingLogs } from './commands/showLogs';
+import { stopWatchingLogs } from './commands/showLogs';
import { ContextManager } from './context/contextManager';
import {
getEnabledSmells,
@@ -17,23 +17,31 @@ import { updateHash } from './utils/hashDocs';
import { RefactorSidebarProvider } from './ui/refactorView';
import { handleEditorChanges } from './utils/handleEditorChange';
import { LineSelectionManager } from './ui/lineSelectionManager';
+import { checkServerStatus } from './api/backend';
+import { serverStatus } from './utils/serverStatus';
+
+export const globalData: { contextManager?: ContextManager } = {
+ contextManager: undefined
+};
export function activate(context: vscode.ExtensionContext) {
console.log('Eco: Refactor Plugin Activated Successfully');
const contextManager = new ContextManager(context);
+ globalData.contextManager = contextManager;
+
// Show the settings popup if needed
// TODO: Setting to re-enable popup if disabled
const settingsPopupChoice =
contextManager.getGlobalData('showSettingsPopup');
if (settingsPopupChoice === undefined || settingsPopupChoice) {
- showSettingsPopup(contextManager);
+ showSettingsPopup();
}
console.log('environment variables:', envConfig);
- startLogging(context);
+ checkServerStatus();
let smellsData = contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!) || {};
contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, smellsData);
@@ -42,6 +50,9 @@ export function activate(context: vscode.ExtensionContext) {
contextManager.getWorkspaceData(envConfig.FILE_CHANGES_KEY!) || {};
contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, fileHashes);
+ // Check server health every 10 seconds
+ setInterval(checkServerStatus, 10000);
+
// ===============================================================
// REGISTER COMMANDS
// ===============================================================
@@ -62,8 +73,12 @@ export function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand(
'ecooptimizer-vs-code-plugin.refactorSmell',
() => {
- console.log('Eco: Refactor Selected Smell Command Triggered');
- refactorSelectedSmell(contextManager);
+ if (serverStatus.getStatus() === 'up') {
+ console.log('Eco: Refactor Selected Smell Command Triggered');
+ refactorSelectedSmell(contextManager);
+ } else {
+ vscode.window.showWarningMessage('Action blocked: Server is down.');
+ }
}
)
);
@@ -73,10 +88,14 @@ export function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand(
'ecooptimizer-vs-code-plugin.refactorAllSmellsOfType',
async (smellId: string) => {
- console.log(
- `Eco: Refactor All Smells of Type Command Triggered for ${smellId}`
- );
- refactorAllSmellsOfType(contextManager, smellId);
+ if (serverStatus.getStatus() === 'up') {
+ console.log(
+ `Eco: Refactor All Smells of Type Command Triggered for ${smellId}`
+ );
+ refactorAllSmellsOfType(contextManager, smellId);
+ } else {
+ vscode.window.showWarningMessage('Action blocked: Server is down.');
+ }
}
)
);
@@ -189,7 +208,7 @@ export function activate(context: vscode.ExtensionContext) {
});
}
-function showSettingsPopup(contextManager: ContextManager) {
+function showSettingsPopup() {
// Check if the required settings are already configured
const config = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin');
const workspacePath = config.get('projectWorkspacePath', '');
@@ -219,7 +238,7 @@ function showSettingsPopup(contextManager: ContextManager) {
'You can configure the paths later in the settings.'
);
} else if (selection === 'Never show this again') {
- contextManager.setGlobalData('showSettingsPopup', false);
+ globalData.contextManager!.setGlobalData('showSettingsPopup', false);
vscode.window.showInformationMessage(
'You can re-enable this popup again in the settings.'
);
diff --git a/src/utils/serverStatus.ts b/src/utils/serverStatus.ts
new file mode 100644
index 0000000..a7bf956
--- /dev/null
+++ b/src/utils/serverStatus.ts
@@ -0,0 +1,29 @@
+import * as vscode from 'vscode';
+import { EventEmitter } from 'events';
+
+class ServerStatus extends EventEmitter {
+ private status: 'unknown' | 'up' | 'down' = 'unknown';
+
+ getStatus() {
+ return this.status;
+ }
+
+ setStatus(newStatus: 'up' | 'down') {
+ if (this.status !== newStatus) {
+ if (newStatus === 'up') {
+ if (this.status !== 'unknown') {
+ vscode.window.showInformationMessage(
+ 'Server connection re-established. Smell detection and refactoring functionality resumed.'
+ );
+ }
+ } else {
+ vscode.window.showWarningMessage('Server connection lost.');
+ }
+ this.status = newStatus;
+ this.emit('change', newStatus); // Notify listeners
+ }
+ }
+}
+
+// Singleton instance
+export const serverStatus = new ServerStatus();
From 9eb07cd138bc8e1fab3203ec77943a4d423dc7bf Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 22 Feb 2025 09:33:39 -0500
Subject: [PATCH 45/83] add eslint config
---
.eslintrc | 44 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 .eslintrc
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..032137f
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,44 @@
+{
+ "root": true,
+ "parser": "@typescript-eslint/parser",
+ "plugins": ["@typescript-eslint", "eslint-plugin-import", "eslint-plugin-jsdoc", "eslint-plugin-prefer-arrow"],
+ "extends": [
+ "eslint:recommended",
+ "plugin:@typescript-eslint/recommended",
+ "plugin:@typescript-eslint/recommended-requiring-type-checking",
+ "plugin:import/recommended",
+ "plugin:import/typescript",
+ "plugin:prettier/recommended"
+ ],
+ "parserOptions": {
+ "project": "./tsconfig.json",
+ "sourceType": "module"
+ },
+ "rules": {
+ "@typescript-eslint/explicit-module-boundary-types": "error",
+ "@typescript-eslint/explicit-function-return-type": "error",
+ "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
+ "@typescript-eslint/no-explicit-any": "error",
+ "@typescript-eslint/no-unnecessary-type-assertion": "warn",
+ "@typescript-eslint/strict-boolean-expressions": "warn",
+ "@typescript-eslint/no-floating-promises": "error",
+ "@typescript-eslint/prefer-nullish-coalescing": "warn",
+ "import/order": ["error", { "alphabetize": { "order": "asc" }, "groups": ["builtin", "external", "internal", "parent", "sibling", "index"] }],
+ "prefer-arrow/prefer-arrow-functions": [
+ "error",
+ {
+ "allowStandaloneDeclarations": true
+ }
+ ],
+ "prettier/prettier": "error"
+ },
+ "overrides": [
+ {
+ "files": ["*.test.ts"],
+ "rules": {
+ "@typescript-eslint/no-explicit-any": "off"
+ }
+ }
+ ]
+ }
+
\ No newline at end of file
From 89546f2b79aa75a479a5180e0afd220fbb328449 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 22 Feb 2025 14:13:06 -0500
Subject: [PATCH 46/83] added lint + format pre-commit
---
.eslintrc | 44 --
.husky/pre-commit | 1 +
.prettierignore | 8 +
.prettierrc | 11 +-
.vscode/settings.json | 11 +-
eslint.config.mjs | 77 ++-
package-lock.json | 1099 ++++++++++++++++++++++++++++++++++-------
package.json | 22 +-
8 files changed, 1029 insertions(+), 244 deletions(-)
delete mode 100644 .eslintrc
create mode 100644 .husky/pre-commit
create mode 100644 .prettierignore
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 032137f..0000000
--- a/.eslintrc
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "root": true,
- "parser": "@typescript-eslint/parser",
- "plugins": ["@typescript-eslint", "eslint-plugin-import", "eslint-plugin-jsdoc", "eslint-plugin-prefer-arrow"],
- "extends": [
- "eslint:recommended",
- "plugin:@typescript-eslint/recommended",
- "plugin:@typescript-eslint/recommended-requiring-type-checking",
- "plugin:import/recommended",
- "plugin:import/typescript",
- "plugin:prettier/recommended"
- ],
- "parserOptions": {
- "project": "./tsconfig.json",
- "sourceType": "module"
- },
- "rules": {
- "@typescript-eslint/explicit-module-boundary-types": "error",
- "@typescript-eslint/explicit-function-return-type": "error",
- "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
- "@typescript-eslint/no-explicit-any": "error",
- "@typescript-eslint/no-unnecessary-type-assertion": "warn",
- "@typescript-eslint/strict-boolean-expressions": "warn",
- "@typescript-eslint/no-floating-promises": "error",
- "@typescript-eslint/prefer-nullish-coalescing": "warn",
- "import/order": ["error", { "alphabetize": { "order": "asc" }, "groups": ["builtin", "external", "internal", "parent", "sibling", "index"] }],
- "prefer-arrow/prefer-arrow-functions": [
- "error",
- {
- "allowStandaloneDeclarations": true
- }
- ],
- "prettier/prettier": "error"
- },
- "overrides": [
- {
- "files": ["*.test.ts"],
- "rules": {
- "@typescript-eslint/no-explicit-any": "off"
- }
- }
- ]
- }
-
\ No newline at end of file
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..d0a7784
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+npx lint-staged
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..e3aad94
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,8 @@
+node_modules
+dist
+.vscode
+.husky
+media
+.env
+**/*.config.js
+**/*.mjs'
diff --git a/.prettierrc b/.prettierrc
index bad8f54..69014df 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,7 +1,10 @@
{
- "singleQuote": true,
- "endOfLine": "auto",
- "trailingComma": "none",
"bracketSpacing": true,
- "printWidth": 85
+ "endOfLine": "lf",
+ "printWidth": 85,
+ "semi": true,
+ "singleQuote": true,
+ "tabWidth": 2,
+ "trailingComma": "all",
+ "plugins": ["prettier-plugin-tailwindcss"]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 1a5f10e..d962edb 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -13,5 +13,14 @@
"cSpell.words": [
"ecooptimizer",
"occurences"
- ]
+ ],
+ "eslint.enable": true,
+ "eslint.lintTask.enable": true,
+ "eslint.validate": [
+ "javascript",
+ "typescript",
+ "javascriptreact",
+ "typescriptreact"
+ ],
+ "files.eol": "\n",
}
diff --git a/eslint.config.mjs b/eslint.config.mjs
index d5c0b53..917c23d 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -1,28 +1,63 @@
-import typescriptEslint from "@typescript-eslint/eslint-plugin";
-import tsParser from "@typescript-eslint/parser";
+import js from '@eslint/js';
+import typescriptEslint from '@typescript-eslint/eslint-plugin';
+import tsParser from '@typescript-eslint/parser';
+import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
+import eslintConfigPrettier from 'eslint-config-prettier';
+import unusedImports from 'eslint-plugin-unused-imports';
-export default [{
- files: ["**/*.ts"],
-}, {
+export default [
+ {
+ ignores: [
+ '**/*.mjs',
+ '**/node_modules/**',
+ '**/dist/**',
+ '**/coverage/**',
+ '**/.vscode/**',
+ '**/media/**',
+ '**/.husky/**',
+ '**/*.config.js',
+ ],
+ },
+ js.configs.recommended,
+ {
+ files: ['**/*.ts'],
plugins: {
- "@typescript-eslint": typescriptEslint,
+ '@typescript-eslint': typescriptEslint,
+ 'unused-imports': unusedImports,
},
-
languageOptions: {
- parser: tsParser,
- ecmaVersion: 2022,
- sourceType: "module",
+ parser: tsParser,
+ ecmaVersion: 2022,
+ sourceType: 'module',
},
-
rules: {
- "@typescript-eslint/naming-convention": ["warn", {
- selector: "import",
- format: ["camelCase", "PascalCase"],
- }],
-
- curly: "warn",
- eqeqeq: "warn",
- "no-throw-literal": "warn",
- semi: "warn",
+ 'no-undef': 'off',
+ 'prettier/prettier': 'error',
+ '@typescript-eslint/no-unused-vars': 'warn',
+ '@typescript-eslint/explicit-function-return-type': 'warn',
+ '@typescript-eslint/naming-convention': [
+ 'warn',
+ {
+ selector: 'import',
+ format: ['camelCase', 'PascalCase'],
+ },
+ ],
+ curly: 'warn',
+ eqeqeq: 'warn',
+ 'no-throw-literal': 'warn',
+ semi: 'warn',
+ 'unused-imports/no-unused-imports': 'error',
+ 'unused-imports/no-unused-vars': [
+ 'warn',
+ {
+ vars: 'all',
+ varsIgnorePattern: '^_',
+ args: 'after-used',
+ argsIgnorePattern: '^_',
+ },
+ ],
},
-}];
\ No newline at end of file
+ },
+ eslintPluginPrettierRecommended,
+ eslintConfigPrettier,
+];
diff --git a/package-lock.json b/package-lock.json
index 525d18f..df04607 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,12 +20,19 @@
"@types/node": "20.x",
"@types/vscode": "^1.92.0",
"@types/ws": "^8.5.14",
- "@typescript-eslint/eslint-plugin": "^8.17.0",
- "@typescript-eslint/parser": "^8.17.0",
+ "@typescript-eslint/eslint-plugin": "^8.24.1",
+ "@typescript-eslint/parser": "^8.24.1",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
"css-loader": "^7.1.2",
- "eslint": "^9.16.0",
+ "eslint": "^9.21.0",
+ "eslint-config-prettier": "^10.0.1",
+ "eslint-plugin-prettier": "^5.2.3",
+ "eslint-plugin-unused-imports": "^4.1.4",
+ "husky": "^9.1.7",
+ "lint-staged": "^15.4.3",
+ "prettier": "^3.5.2",
+ "prettier-plugin-tailwindcss": "^0.6.11",
"style-loader": "^4.0.0",
"ts-loader": "^9.5.1",
"typescript": "^5.7.2",
@@ -83,13 +90,12 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz",
- "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
+ "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
"dev": true,
- "license": "Apache-2.0",
"dependencies": {
- "@eslint/object-schema": "^2.1.5",
+ "@eslint/object-schema": "^2.1.6",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
},
@@ -102,7 +108,6 @@
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -113,7 +118,6 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
- "license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -122,11 +126,10 @@
}
},
"node_modules/@eslint/core": {
- "version": "0.10.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz",
- "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==",
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
+ "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
"dev": true,
- "license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
@@ -135,11 +138,10 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz",
- "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
+ "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@@ -163,7 +165,6 @@
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -174,7 +175,6 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
- "license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -183,33 +183,30 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.18.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz",
- "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==",
+ "version": "9.21.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz",
+ "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==",
"dev": true,
- "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/object-schema": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz",
- "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==",
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
"dev": true,
- "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz",
- "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==",
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
+ "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
"dev": true,
- "license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.10.0",
+ "@eslint/core": "^0.12.0",
"levn": "^0.4.1"
},
"engines": {
@@ -269,11 +266,10 @@
}
},
"node_modules/@humanwhocodes/retry": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz",
- "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==",
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
+ "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
"dev": true,
- "license": "Apache-2.0",
"engines": {
"node": ">=18.18"
},
@@ -373,7 +369,6 @@
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@@ -387,7 +382,6 @@
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">= 8"
}
@@ -397,7 +391,6 @@
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@@ -417,6 +410,18 @@
"node": ">=14"
}
},
+ "node_modules/@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
"node_modules/@types/dotenv": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
@@ -498,21 +503,20 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz",
- "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==",
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz",
+ "integrity": "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.20.0",
- "@typescript-eslint/type-utils": "8.20.0",
- "@typescript-eslint/utils": "8.20.0",
- "@typescript-eslint/visitor-keys": "8.20.0",
+ "@typescript-eslint/scope-manager": "8.24.1",
+ "@typescript-eslint/type-utils": "8.24.1",
+ "@typescript-eslint/utils": "8.24.1",
+ "@typescript-eslint/visitor-keys": "8.24.1",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^2.0.0"
+ "ts-api-utils": "^2.0.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -528,16 +532,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz",
- "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==",
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.1.tgz",
+ "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.20.0",
- "@typescript-eslint/types": "8.20.0",
- "@typescript-eslint/typescript-estree": "8.20.0",
- "@typescript-eslint/visitor-keys": "8.20.0",
+ "@typescript-eslint/scope-manager": "8.24.1",
+ "@typescript-eslint/types": "8.24.1",
+ "@typescript-eslint/typescript-estree": "8.24.1",
+ "@typescript-eslint/visitor-keys": "8.24.1",
"debug": "^4.3.4"
},
"engines": {
@@ -553,14 +556,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz",
- "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==",
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz",
+ "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.20.0",
- "@typescript-eslint/visitor-keys": "8.20.0"
+ "@typescript-eslint/types": "8.24.1",
+ "@typescript-eslint/visitor-keys": "8.24.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -571,16 +573,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz",
- "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==",
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.1.tgz",
+ "integrity": "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.20.0",
- "@typescript-eslint/utils": "8.20.0",
+ "@typescript-eslint/typescript-estree": "8.24.1",
+ "@typescript-eslint/utils": "8.24.1",
"debug": "^4.3.4",
- "ts-api-utils": "^2.0.0"
+ "ts-api-utils": "^2.0.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -595,11 +596,10 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz",
- "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==",
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz",
+ "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==",
"dev": true,
- "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -609,20 +609,19 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz",
- "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==",
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz",
+ "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.20.0",
- "@typescript-eslint/visitor-keys": "8.20.0",
+ "@typescript-eslint/types": "8.24.1",
+ "@typescript-eslint/visitor-keys": "8.24.1",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
- "ts-api-utils": "^2.0.0"
+ "ts-api-utils": "^2.0.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -636,16 +635,15 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz",
- "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==",
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz",
+ "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.20.0",
- "@typescript-eslint/types": "8.20.0",
- "@typescript-eslint/typescript-estree": "8.20.0"
+ "@typescript-eslint/scope-manager": "8.24.1",
+ "@typescript-eslint/types": "8.24.1",
+ "@typescript-eslint/typescript-estree": "8.24.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -660,13 +658,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz",
- "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==",
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz",
+ "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.20.0",
+ "@typescript-eslint/types": "8.24.1",
"eslint-visitor-keys": "^4.2.0"
},
"engines": {
@@ -682,7 +679,6 @@
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true,
- "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -953,7 +949,6 @@
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true,
- "license": "MIT",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
@@ -1042,6 +1037,21 @@
"node": ">=6"
}
},
+ "node_modules/ansi-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/ansi-regex": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
@@ -1296,7 +1306,6 @@
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -1427,6 +1436,45 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/emoji-regex": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+ "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+ "dev": true
+ },
+ "node_modules/cli-truncate/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -1772,6 +1820,18 @@
"node": ">=4"
}
},
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/es-module-lexer": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
@@ -1801,22 +1861,21 @@
}
},
"node_modules/eslint": {
- "version": "9.18.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz",
- "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==",
+ "version": "9.21.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
+ "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.19.0",
- "@eslint/core": "^0.10.0",
- "@eslint/eslintrc": "^3.2.0",
- "@eslint/js": "9.18.0",
- "@eslint/plugin-kit": "^0.2.5",
+ "@eslint/config-array": "^0.19.2",
+ "@eslint/core": "^0.12.0",
+ "@eslint/eslintrc": "^3.3.0",
+ "@eslint/js": "9.21.0",
+ "@eslint/plugin-kit": "^0.2.7",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.1",
+ "@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
@@ -1860,6 +1919,63 @@
}
}
},
+ "node_modules/eslint-config-prettier": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz",
+ "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "build/bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz",
+ "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.9.1"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": "*",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-unused-imports": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz",
+ "integrity": "sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==",
+ "dev": true,
+ "peerDependencies": {
+ "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0",
+ "eslint": "^9.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/eslint-plugin": {
+ "optional": true
+ }
+ }
+ },
"node_modules/eslint-scope": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
@@ -1945,7 +2061,6 @@
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
"dev": true,
- "license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.14.0",
"acorn-jsx": "^5.3.2",
@@ -1963,7 +2078,6 @@
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true,
- "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -2015,6 +2129,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true
+ },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
@@ -2024,18 +2144,73 @@
"node": ">=0.8.x"
}
},
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/execa/node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/execa/node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@@ -2087,11 +2262,10 @@
}
},
"node_modules/fastq": {
- "version": "1.18.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
- "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==",
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
+ "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
"dev": true,
- "license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
}
@@ -2229,6 +2403,30 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/get-east-asian-width": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+ "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -2274,7 +2472,6 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=18"
},
@@ -2362,6 +2559,30 @@
"node": ">= 14"
}
},
+ "node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/husky": {
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
+ "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
+ "dev": true,
+ "bin": {
+ "husky": "bin.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
"node_modules/icss-utils": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
@@ -2413,11 +2634,10 @@
"license": "MIT"
},
"node_modules/import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -2596,6 +2816,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-unicode-supported": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
@@ -2826,66 +3058,343 @@
"immediate": "~3.0.5"
}
},
- "node_modules/loader-runner": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
- "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
- "license": "MIT",
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
"engines": {
- "node": ">=6.11.5"
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
}
},
- "node_modules/locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "node_modules/lint-staged": {
+ "version": "15.4.3",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.4.3.tgz",
+ "integrity": "sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "p-locate": "^5.0.0"
+ "chalk": "^5.4.1",
+ "commander": "^13.1.0",
+ "debug": "^4.4.0",
+ "execa": "^8.0.1",
+ "lilconfig": "^3.1.3",
+ "listr2": "^8.2.5",
+ "micromatch": "^4.0.8",
+ "pidtree": "^0.6.0",
+ "string-argv": "^0.3.2",
+ "yaml": "^2.7.0"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
},
"engines": {
- "node": ">=10"
+ "node": ">=18.12.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://opencollective.com/lint-staged"
}
},
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/log-symbols": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
- "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "node_modules/lint-staged/node_modules/chalk": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+ "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "chalk": "^4.1.0",
- "is-unicode-supported": "^0.1.0"
- },
"engines": {
- "node": ">=10"
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "node_modules/lru-cache": {
- "version": "10.4.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
- "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "node_modules/lint-staged/node_modules/commander": {
+ "version": "13.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
+ "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
"dev": true,
- "license": "ISC"
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/make-dir": {
- "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==",
+ "node_modules/listr2": {
+ "version": "8.2.5",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz",
+ "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==",
+ "dev": true,
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/listr2/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/emoji-regex": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+ "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+ "dev": true
+ },
+ "node_modules/listr2/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/listr2/node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/loader-runner": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.11.5"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-symbols": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/emoji-regex": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+ "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+ "dev": true
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
+ "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+ "dev": true,
+ "dependencies": {
+ "get-east-asian-width": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
+ "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/lru-cache": {
+ "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/make-dir": {
+ "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": {
@@ -2909,7 +3418,6 @@
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">= 8"
}
@@ -2959,6 +3467,18 @@
"node": ">=6"
}
},
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -3239,6 +3759,33 @@
"node": ">=0.10.0"
}
},
+ "node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -3436,7 +3983,6 @@
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
- "license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
},
@@ -3517,6 +4063,18 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pidtree": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+ "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+ "dev": true,
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -3702,6 +4260,111 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz",
+ "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/prettier-plugin-tailwindcss": {
+ "version": "0.6.11",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.11.tgz",
+ "integrity": "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.21.3"
+ },
+ "peerDependencies": {
+ "@ianvs/prettier-plugin-sort-imports": "*",
+ "@prettier/plugin-pug": "*",
+ "@shopify/prettier-plugin-liquid": "*",
+ "@trivago/prettier-plugin-sort-imports": "*",
+ "@zackad/prettier-plugin-twig": "*",
+ "prettier": "^3.0",
+ "prettier-plugin-astro": "*",
+ "prettier-plugin-css-order": "*",
+ "prettier-plugin-import-sort": "*",
+ "prettier-plugin-jsdoc": "*",
+ "prettier-plugin-marko": "*",
+ "prettier-plugin-multiline-arrays": "*",
+ "prettier-plugin-organize-attributes": "*",
+ "prettier-plugin-organize-imports": "*",
+ "prettier-plugin-sort-imports": "*",
+ "prettier-plugin-style-order": "*",
+ "prettier-plugin-svelte": "*"
+ },
+ "peerDependenciesMeta": {
+ "@ianvs/prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "@prettier/plugin-pug": {
+ "optional": true
+ },
+ "@shopify/prettier-plugin-liquid": {
+ "optional": true
+ },
+ "@trivago/prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "@zackad/prettier-plugin-twig": {
+ "optional": true
+ },
+ "prettier-plugin-astro": {
+ "optional": true
+ },
+ "prettier-plugin-css-order": {
+ "optional": true
+ },
+ "prettier-plugin-import-sort": {
+ "optional": true
+ },
+ "prettier-plugin-jsdoc": {
+ "optional": true
+ },
+ "prettier-plugin-marko": {
+ "optional": true
+ },
+ "prettier-plugin-multiline-arrays": {
+ "optional": true
+ },
+ "prettier-plugin-organize-attributes": {
+ "optional": true
+ },
+ "prettier-plugin-organize-imports": {
+ "optional": true
+ },
+ "prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "prettier-plugin-style-order": {
+ "optional": true
+ },
+ "prettier-plugin-svelte": {
+ "optional": true
+ }
+ }
+ },
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -3736,8 +4399,7 @@
"type": "consulting",
"url": "https://feross.org/support"
}
- ],
- "license": "MIT"
+ ]
},
"node_modules/randombytes": {
"version": "2.1.0",
@@ -3858,7 +4520,6 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=4"
}
@@ -3892,12 +4553,17 @@
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true,
- "license": "MIT",
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
}
},
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -3917,7 +4583,6 @@
"url": "https://feross.org/support"
}
],
- "license": "MIT",
"dependencies": {
"queue-microtask": "^1.2.2"
}
@@ -4024,6 +4689,46 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@@ -4088,6 +4793,15 @@
"safe-buffer": "~5.1.0"
}
},
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -4192,6 +4906,18 @@
"node": ">=8"
}
},
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -4247,6 +4973,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/synckit": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
+ "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
+ "dev": true,
+ "dependencies": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -4436,11 +5178,10 @@
}
},
"node_modules/ts-api-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz",
- "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
+ "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=18.12"
},
@@ -4469,6 +5210,12 @@
"webpack": "^5.0.0"
}
},
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -4908,6 +5655,18 @@
"node": ">=10"
}
},
+ "node_modules/yaml": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
+ "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==",
+ "dev": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
diff --git a/package.json b/package.json
index 6ac05b0..ab07abe 100644
--- a/package.json
+++ b/package.json
@@ -167,19 +167,33 @@
"watch-tests": "tsc -p . -w --outDir out",
"pretest": "npm run compile-tests && npm run compile && npm run lint",
"lint": "eslint src",
- "test": "vscode-test"
+ "test": "vscode-test",
+ "prepare": "husky"
+ },
+ "lint-staged": {
+ "src/**/*.ts": [
+ "eslint --fix",
+ "prettier --write"
+ ]
},
"devDependencies": {
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.92.0",
"@types/ws": "^8.5.14",
- "@typescript-eslint/eslint-plugin": "^8.17.0",
- "@typescript-eslint/parser": "^8.17.0",
+ "@typescript-eslint/eslint-plugin": "^8.24.1",
+ "@typescript-eslint/parser": "^8.24.1",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
"css-loader": "^7.1.2",
- "eslint": "^9.16.0",
+ "eslint": "^9.21.0",
+ "eslint-config-prettier": "^10.0.1",
+ "eslint-plugin-prettier": "^5.2.3",
+ "eslint-plugin-unused-imports": "^4.1.4",
+ "husky": "^9.1.7",
+ "lint-staged": "^15.4.3",
+ "prettier": "^3.5.2",
+ "prettier-plugin-tailwindcss": "^0.6.11",
"style-loader": "^4.0.0",
"ts-loader": "^9.5.1",
"typescript": "^5.7.2",
From 8f59f58537e1ad45495f222318ad0a7b7442aa8e Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 22 Feb 2025 15:15:27 -0500
Subject: [PATCH 47/83] updated linting configuration
---
eslint.config.mjs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 917c23d..010c150 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -1,5 +1,5 @@
import js from '@eslint/js';
-import typescriptEslint from '@typescript-eslint/eslint-plugin';
+import tseslint from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import eslintConfigPrettier from 'eslint-config-prettier';
@@ -22,7 +22,7 @@ export default [
{
files: ['**/*.ts'],
plugins: {
- '@typescript-eslint': typescriptEslint,
+ '@typescript-eslint': tseslint,
'unused-imports': unusedImports,
},
languageOptions: {
@@ -32,8 +32,8 @@ export default [
},
rules: {
'no-undef': 'off',
+ 'no-unused-vars': 'off',
'prettier/prettier': 'error',
- '@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/explicit-function-return-type': 'warn',
'@typescript-eslint/naming-convention': [
'warn',
From bd4a09aed7ade6cc7116aaecc2b84349b51095f8 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 22 Feb 2025 15:16:27 -0500
Subject: [PATCH 48/83] fixed issues raised by linter
---
src/api/backend.ts | 40 +++++++--------
src/commands/detectSmells.ts | 18 +++----
src/commands/refactorSmell.ts | 81 +++++++++++++++---------------
src/commands/showLogs.ts | 16 +++---
src/commands/wipeWorkCache.ts | 6 +--
src/context/contextManager.ts | 4 +-
src/extension.ts | 68 ++++++++++++-------------
src/types.ts | 2 -
src/ui/diffViewer.ts | 86 --------------------------------
src/ui/fileHighlighter.ts | 24 ++++-----
src/ui/hoverManager.ts | 41 ++++++++-------
src/ui/lineSelectionManager.ts | 12 ++---
src/ui/refactorManager.ts | 19 -------
src/ui/refactorView.ts | 45 +++++++++--------
src/utils/configManager.ts | 6 +--
src/utils/envConfig.ts | 2 +-
src/utils/handleEditorChange.ts | 32 ++++++------
src/utils/handleSmellSettings.ts | 6 +--
src/utils/hashDocs.ts | 8 +--
src/utils/serverStatus.ts | 18 ++++---
src/utils/smellDetails.ts | 38 +++++++-------
21 files changed, 237 insertions(+), 335 deletions(-)
delete mode 100644 src/ui/diffViewer.ts
delete mode 100644 src/ui/refactorManager.ts
diff --git a/src/api/backend.ts b/src/api/backend.ts
index c27d2a4..2891f93 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -6,7 +6,7 @@ import { serverStatus } from '../utils/serverStatus';
const BASE_URL = `http://${envConfig.SERVER_URL}`; // API URL for Python backend
-export async function checkServerStatus() {
+export async function checkServerStatus(): Promise {
try {
const response = await fetch('http://localhost:8000/health');
if (response.ok) {
@@ -19,7 +19,7 @@ export async function checkServerStatus() {
}
}
-export async function initLogs(log_dir: string) {
+export async function initLogs(log_dir: string): Promise {
const url = `${BASE_URL}/logs/init`;
try {
@@ -28,9 +28,9 @@ export async function initLogs(log_dir: string) {
const response = await fetch(url, {
method: 'POST',
headers: {
- 'Content-Type': 'application/json'
+ 'Content-Type': 'application/json',
},
- body: JSON.stringify({ log_dir })
+ body: JSON.stringify({ log_dir }),
});
if (!response.ok) {
@@ -43,7 +43,7 @@ export async function initLogs(log_dir: string) {
} catch (error: any) {
console.error(`Eco: Unable to initialize logging: ${error.message}`);
vscode.window.showErrorMessage(
- 'Eco: Unable to reach the backend. Please check your connection.'
+ 'Eco: Unable to reach the backend. Please check your connection.',
);
return false;
}
@@ -52,29 +52,29 @@ export async function initLogs(log_dir: string) {
// β
Fetch detected smells for a given file (only enabled smells)
export async function fetchSmells(
filePath: string,
- enabledSmells: string[]
+ enabledSmells: string[],
): Promise {
const url = `${BASE_URL}/smells`;
try {
console.log(
- `Eco: Requesting smells for file: ${filePath} with filters: ${enabledSmells}`
+ `Eco: Requesting smells for file: ${filePath} with filters: ${enabledSmells}`,
);
const response = await fetch(url, {
method: 'POST', // β
Send enabled smells in the request body
headers: {
- 'Content-Type': 'application/json'
+ 'Content-Type': 'application/json',
},
- body: JSON.stringify({ file_path: filePath, enabled_smells: enabledSmells }) // β
Include enabled smells
+ body: JSON.stringify({ file_path: filePath, enabled_smells: enabledSmells }), // β
Include enabled smells
});
if (!response.ok) {
console.error(
- `Eco: API request failed (${response.status} - ${response.statusText})`
+ `Eco: API request failed (${response.status} - ${response.statusText})`,
);
vscode.window.showErrorMessage(
- `Eco: Failed to fetch smells (HTTP ${response.status})`
+ `Eco: Failed to fetch smells (HTTP ${response.status})`,
);
return [];
}
@@ -92,7 +92,7 @@ export async function fetchSmells(
} catch (error: any) {
console.error(`Eco: Network error while fetching smells: ${error.message}`);
vscode.window.showErrorMessage(
- 'Eco: Unable to reach the backend. Please check your connection.'
+ 'Eco: Unable to reach the backend. Please check your connection.',
);
return [];
}
@@ -101,43 +101,43 @@ export async function fetchSmells(
// Request refactoring for a specific smell
export async function refactorSmell(
filePath: string,
- smell: Smell
+ smell: Smell,
): Promise {
const url = `${BASE_URL}/refactor`;
const workspace_folder = vscode.workspace.workspaceFolders?.find((folder) =>
- filePath.includes(folder.uri.fsPath)
+ filePath.includes(folder.uri.fsPath),
)?.uri.fsPath;
if (!workspace_folder) {
console.error('Eco: Error - Unable to determine workspace folder for', filePath);
throw new Error(
- `Eco: Unable to find a matching workspace folder for file: ${filePath}`
+ `Eco: Unable to find a matching workspace folder for file: ${filePath}`,
);
}
console.log(
- `Eco: Initiating refactoring for smell "${smell.symbol}" in "${workspace_folder}"`
+ `Eco: Initiating refactoring for smell "${smell.symbol}" in "${workspace_folder}"`,
);
const payload = {
source_dir: workspace_folder,
- smell
+ smell,
};
try {
const response = await fetch(url, {
method: 'POST',
headers: {
- 'Content-Type': 'application/json'
+ 'Content-Type': 'application/json',
},
- body: JSON.stringify(payload)
+ body: JSON.stringify(payload),
});
if (!response.ok) {
const errorText = await response.text();
console.error(
- `Eco: Error - Refactoring smell "${smell.symbol}": ${errorText}`
+ `Eco: Error - Refactoring smell "${smell.symbol}": ${errorText}`,
);
throw new Error(`Eco: Error refactoring smell: ${errorText}`);
}
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 030e09f..82bf030 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -17,7 +17,7 @@ serverStatus.on('change', (newStatus) => {
if (newStatus === 'down') {
serverOn = false;
vscode.window.showWarningMessage(
- 'Smell detection limited. Only cached smells will be shown.'
+ 'Smell detection limited. Only cached smells will be shown.',
);
} else {
serverOn = true;
@@ -31,7 +31,7 @@ export interface SmellDetectRecord {
let fileHighlighter: FileHighlighter;
-export async function detectSmells(contextManager: ContextManager) {
+export async function detectSmells(contextManager: ContextManager): Promise {
const { editor, filePath } = getEditorAndFilePath();
// β
Ensure an active editor exists
@@ -53,12 +53,12 @@ export async function detectSmells(contextManager: ContextManager) {
// β
Fetch user-enabled smells
const enabledSmells = getEnabledSmells();
const activeSmells = Object.keys(enabledSmells).filter(
- (smell) => enabledSmells[smell]
+ (smell) => enabledSmells[smell],
);
if (activeSmells.length === 0) {
vscode.window.showWarningMessage(
- 'Eco: No smells are enabled! Detection skipped.'
+ 'Eco: No smells are enabled! Detection skipped.',
);
console.warn('Eco: No smells are enabled. Detection will not proceed.');
return;
@@ -67,7 +67,7 @@ export async function detectSmells(contextManager: ContextManager) {
// β
Check if the enabled smells have changed
const lastUsedSmells = contextManager.getWorkspaceData(
envConfig.LAST_USED_SMELLS_KEY!,
- {}
+ {},
);
if (JSON.stringify(lastUsedSmells) !== JSON.stringify(enabledSmells)) {
console.log('Eco: Smell settings have changed! Wiping cache.');
@@ -85,7 +85,7 @@ export async function detectSmells(contextManager: ContextManager) {
// β
Function to fetch smells and update cache
async function fetchAndStoreSmells(): Promise {
console.log(
- `Eco: Fetching smells from backend for file: ${filePath} with filters: ${activeSmells}`
+ `Eco: Fetching smells from backend for file: ${filePath} with filters: ${activeSmells}`,
);
if (!filePath) {
@@ -102,7 +102,7 @@ export async function detectSmells(contextManager: ContextManager) {
}
console.log(
- `Eco: ${smellsData.length} smells found in ${filePath}. Updating cache.`
+ `Eco: ${smellsData.length} smells found in ${filePath}. Updating cache.`,
);
// β
Ensure safe update of smells cache
@@ -130,7 +130,7 @@ export async function detectSmells(contextManager: ContextManager) {
smellsData = await fetchAndStoreSmells();
} else {
vscode.window.showWarningMessage(
- 'Action blocked: Server is down and no cached smells exists for this file version.'
+ 'Action blocked: Server is down and no cached smells exists for this file version.',
);
return;
}
@@ -147,7 +147,7 @@ export async function detectSmells(contextManager: ContextManager) {
fileHighlighter.highlightSmells(editor, smellsData);
vscode.window.showInformationMessage(
- `Eco: Highlighted ${smellsData.length} smells.`
+ `Eco: Highlighted ${smellsData.length} smells.`,
);
}
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 8d8b452..6a839c9 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -30,8 +30,7 @@ export interface MultiRefactoredData {
async function refactorLine(
smell: Smell,
filePath: string,
- contextManager: ContextManager
-) {
+): Promise {
try {
const refactorResult = await refactorSmell(filePath, smell);
return refactorResult;
@@ -44,12 +43,12 @@ async function refactorLine(
export async function refactorSelectedSmell(
contextManager: ContextManager,
- smellGiven?: Smell
-) {
+ smellGiven?: Smell,
+): Promise {
const { editor, filePath } = getEditorAndFilePath();
const pastData = contextManager.getWorkspaceData(
- envConfig.CURRENT_REFACTOR_DATA_KEY!
+ envConfig.CURRENT_REFACTOR_DATA_KEY!,
);
// Clean up temp directory if not removed
@@ -59,7 +58,7 @@ export async function refactorSelectedSmell(
if (!editor || !filePath) {
vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as no active editor or file path found.'
+ 'Eco: Unable to proceed as no active editor or file path found.',
);
return;
}
@@ -67,12 +66,12 @@ export async function refactorSelectedSmell(
const selectedLine = editor.selection.start.line + 1; // Update to VS Code editor indexing
const smellsData: Smell[] = contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!
+ envConfig.SMELL_MAP_KEY!,
)[filePath].smells;
if (!smellsData || smellsData.length === 0) {
vscode.window.showErrorMessage(
- 'Eco: No smells detected in the file for refactoring.'
+ 'Eco: No smells detected in the file for refactoring.',
);
return;
}
@@ -83,11 +82,11 @@ export async function refactorSelectedSmell(
smellToRefactor = smellsData.find(
(smell: Smell) =>
smell.messageId === smellGiven.messageId &&
- smellGiven.occurences[0].line === smell.occurences[0].line
+ smellGiven.occurences[0].line === smell.occurences[0].line,
);
} else {
smellToRefactor = smellsData.find(
- (smell: Smell) => selectedLine === smell.occurences[0].line
+ (smell: Smell) => selectedLine === smell.occurences[0].line,
);
}
@@ -99,24 +98,24 @@ export async function refactorSelectedSmell(
const refactorResult = await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
- title: `Fetching refactoring for ${smellToRefactor.symbol} on line ${smellToRefactor.occurences[0].line}`
+ title: `Fetching refactoring for ${smellToRefactor.symbol} on line ${smellToRefactor.occurences[0].line}`,
},
- async (progress, token) => {
- const result = await refactorLine(smellToRefactor, filePath, contextManager);
+ async (_progress, _token) => {
+ const result = await refactorLine(smellToRefactor, filePath);
if (result && result.refactoredData) {
vscode.window.showInformationMessage(
- 'Refactoring report available in sidebar.'
+ 'Refactoring report available in sidebar.',
);
}
return result;
- }
+ },
);
if (!refactorResult || !refactorResult.refactoredData) {
vscode.window.showErrorMessage(
- 'Eco: Refactoring failed. See console for details.'
+ 'Eco: Refactoring failed. See console for details.',
);
return;
}
@@ -130,19 +129,19 @@ export async function refactorSelectedSmell(
fileHighlighter.highlightSmells(editor, refactorResult.updatedSmells);
} else {
vscode.window.showWarningMessage(
- 'Eco: No updated smells detected after refactoring.'
+ 'Eco: No updated smells detected after refactoring.',
);
}
}
export async function refactorAllSmellsOfType(
contextManager: ContextManager,
- smellId: string
-) {
+ smellId: string,
+): Promise {
const { editor, filePath } = getEditorAndFilePath();
const pastData = contextManager.getWorkspaceData(
- envConfig.CURRENT_REFACTOR_DATA_KEY!
+ envConfig.CURRENT_REFACTOR_DATA_KEY!,
);
// Clean up temp directory if not removed
@@ -152,29 +151,29 @@ export async function refactorAllSmellsOfType(
if (!editor) {
vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as no active editor found.'
+ 'Eco: Unable to proceed as no active editor found.',
);
console.log('No active editor found to refactor smell. Returning back.');
return;
}
if (!filePath) {
vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as active editor does not have a valid file path.'
+ 'Eco: Unable to proceed as active editor does not have a valid file path.',
);
console.log('No valid file path found to refactor smell. Returning back.');
return;
}
// only account for one selection to be refactored for now
- const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
+ // const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
const smellsData: Smell[] = contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!
+ envConfig.SMELL_MAP_KEY!,
)[filePath].smells;
if (!smellsData || smellsData.length === 0) {
vscode.window.showErrorMessage(
- 'Eco: No smells detected in the file for refactoring.'
+ 'Eco: No smells detected in the file for refactoring.',
);
console.log('No smells found in the file for refactoring.');
return;
@@ -182,12 +181,12 @@ export async function refactorAllSmellsOfType(
// Filter smells by the given type ID
const smellsOfType = smellsData.filter(
- (smell: Smell) => smell.messageId === smellId
+ (smell: Smell) => smell.messageId === smellId,
);
if (smellsOfType.length === 0) {
vscode.window.showWarningMessage(
- `Eco: No smells of type ${smellId} found in the file.`
+ `Eco: No smells of type ${smellId} found in the file.`,
);
return;
}
@@ -198,7 +197,7 @@ export async function refactorAllSmellsOfType(
// Refactor each smell of the given type
for (const smell of smellsOfType) {
- const refactorResult = await refactorLine(smell, filePath, contextManager);
+ const refactorResult = await refactorLine(smell, filePath);
if (refactorResult && refactorResult.refactoredData) {
// Add two newlines between each refactored result
@@ -212,7 +211,7 @@ export async function refactorAllSmellsOfType(
if (!err) {
combinedRefactoredData += data.toString('utf8');
}
- }
+ },
);
totalEnergySaved += refactorResult.refactoredData.energySaved;
@@ -251,12 +250,12 @@ export async function refactorAllSmellsOfType(
await RefactorManager.previewRefactor(editor, combinedRefactoredData);
vscode.window.showInformationMessage(
`Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(
- 4
- )}`
+ 4,
+ )}`,
);
} else {
vscode.window.showErrorMessage(
- 'Eco: Refactoring failed. See console for details.'
+ 'Eco: Refactoring failed. See console for details.',
);
return;
}
@@ -266,7 +265,7 @@ export async function refactorAllSmellsOfType(
fileHighlighter.highlightSmells(editor, allUpdatedSmells);
} else {
vscode.window.showWarningMessage(
- 'Eco: No updated smells detected after refactoring.'
+ 'Eco: No updated smells detected after refactoring.',
);
}
}
@@ -274,12 +273,12 @@ export async function refactorAllSmellsOfType(
async function startRefactoringSession(
contextManager: ContextManager,
editor: vscode.TextEditor,
- refactoredData: RefactoredData | MultiRefactoredData
-) {
+ refactoredData: RefactoredData | MultiRefactoredData,
+): Promise {
// Store only the diff editor state
await contextManager.setWorkspaceData(
envConfig.CURRENT_REFACTOR_DATA_KEY!,
- refactoredData
+ refactoredData,
);
await vscode.commands.executeCommand('extension.refactorSidebar.focus');
@@ -292,18 +291,18 @@ async function startRefactoringSession(
const allFiles: ChangedFile[] = [
refactoredData.targetFile,
- ...refactoredData.affectedFiles
+ ...refactoredData.affectedFiles,
].map((file) => {
return {
original: vscode.Uri.file(file.original).toString(),
- refactored: vscode.Uri.file(file.refactored).toString()
+ refactored: vscode.Uri.file(file.refactored).toString(),
};
});
await contextManager.setWorkspaceData(envConfig.ACTIVE_DIFF_KEY!, {
files: allFiles,
firstOpen: true,
- isOpen: true
+ isOpen: true,
});
await setTimeout(500);
@@ -317,13 +316,13 @@ async function startRefactoringSession(
'vscode.diff',
originalCode,
refactoredCode,
- 'Refactoring Comparison'
+ 'Refactoring Comparison',
);
vscode.commands.executeCommand('ecooptimizer-vs-code-plugin.showRefactorSidebar');
sidebarState.isOpening = false;
}
-async function cleanTemps(pastData: any) {
+async function cleanTemps(pastData: any): Promise {
console.log('Cleaning up stale artifacts');
const tempDirs =
(pastData!.tempDir! as string) || (pastData!.tempDirs! as string[]);
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index 588492c..bb065eb 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -56,7 +56,7 @@ export async function startLogging(retries = 3, delay = 1000): Promise {
if (!logInitialized) {
throw new LogInitializationError(
- `Failed to initialize logs at path: ${logPath}`
+ `Failed to initialize logs at path: ${logPath}`,
);
}
console.log('Log initialization successful.');
@@ -64,7 +64,7 @@ export async function startLogging(retries = 3, delay = 1000): Promise {
if (CHANNELS_CREATED) {
console.warn(
- 'Logging channels already initialized. Skipping WebSocket setup.'
+ 'Logging channels already initialized. Skipping WebSocket setup.',
);
return;
}
@@ -92,7 +92,7 @@ export async function startLogging(retries = 3, delay = 1000): Promise {
}
}
-function initializeWebSockets() {
+function initializeWebSockets(): void {
startWebSocket('main', 'EcoOptimizer: Main Logs');
startWebSocket('detect', 'EcoOptimizer: Detect Smells');
startWebSocket('refactor', 'EcoOptimizer: Refactor Smell');
@@ -100,7 +100,7 @@ function initializeWebSockets() {
CHANNELS_CREATED = true;
}
-function startWebSocket(logType: string, channelName: string) {
+function startWebSocket(logType: string, channelName: string): void {
const url = `${WEBSOCKET_BASE_URL}/${logType}`;
const ws = new WebSocket(url);
websockets.push(ws);
@@ -123,9 +123,9 @@ function startWebSocket(logType: string, channelName: string) {
channel.append(data.toString('utf8'));
});
- ws.onerror = (event) => {
- channel.appendLine(`WebSocket error: ${event}`);
- };
+ ws.on('error', function error(err) {
+ channel.appendLine(`WebSocket error: ${err}`);
+ });
ws.on('close', function close() {
channel.appendLine(`WebSocket connection closed for ${logType}`);
@@ -139,7 +139,7 @@ function startWebSocket(logType: string, channelName: string) {
/**
* Stops watching log files when the extension is deactivated.
*/
-export function stopWatchingLogs() {
+export function stopWatchingLogs(): void {
websockets.forEach((ws) => ws.close());
mainLogChannel?.dispose();
diff --git a/src/commands/wipeWorkCache.ts b/src/commands/wipeWorkCache.ts
index 9c620e7..eefad39 100644
--- a/src/commands/wipeWorkCache.ts
+++ b/src/commands/wipeWorkCache.ts
@@ -5,8 +5,8 @@ import { updateHash } from '../utils/hashDocs';
export async function wipeWorkCache(
contextManager: ContextManager,
- reason?: string
-) {
+ reason?: string,
+): Promise {
try {
console.log('Eco: Wiping workspace cache...');
@@ -46,7 +46,7 @@ export async function wipeWorkCache(
} catch (error: any) {
console.error('Eco: Error while wiping workspace cache:', error);
vscode.window.showErrorMessage(
- `Eco: Failed to wipe workspace cache. See console for details.`
+ `Eco: Failed to wipe workspace cache. See console for details.`,
);
}
}
diff --git a/src/context/contextManager.ts b/src/context/contextManager.ts
index ed67f95..896255c 100644
--- a/src/context/contextManager.ts
+++ b/src/context/contextManager.ts
@@ -10,7 +10,7 @@ export class ContextManager {
// Global state example
public getGlobalData(
key: string,
- defaultVal: any = undefined
+ defaultVal: any = undefined,
): T | undefined {
return this.context.globalState.get(key, defaultVal);
}
@@ -22,7 +22,7 @@ export class ContextManager {
// Workspace state example
public getWorkspaceData(
key: string,
- defaultVal: any = undefined
+ defaultVal: any = undefined,
): T | undefined {
return this.context.workspaceState.get(key, defaultVal);
}
diff --git a/src/extension.ts b/src/extension.ts
index 8f6912f..9560813 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -4,14 +4,14 @@ import * as vscode from 'vscode';
import { detectSmells } from './commands/detectSmells';
import {
refactorSelectedSmell,
- refactorAllSmellsOfType
+ refactorAllSmellsOfType,
} from './commands/refactorSmell';
import { wipeWorkCache } from './commands/wipeWorkCache';
import { stopWatchingLogs } from './commands/showLogs';
import { ContextManager } from './context/contextManager';
import {
getEnabledSmells,
- handleSmellFilterUpdate
+ handleSmellFilterUpdate,
} from './utils/handleSmellSettings';
import { updateHash } from './utils/hashDocs';
import { RefactorSidebarProvider } from './ui/refactorView';
@@ -21,10 +21,10 @@ import { checkServerStatus } from './api/backend';
import { serverStatus } from './utils/serverStatus';
export const globalData: { contextManager?: ContextManager } = {
- contextManager: undefined
+ contextManager: undefined,
};
-export function activate(context: vscode.ExtensionContext) {
+export function activate(context: vscode.ExtensionContext): void {
console.log('Eco: Refactor Plugin Activated Successfully');
const contextManager = new ContextManager(context);
@@ -64,8 +64,8 @@ export function activate(context: vscode.ExtensionContext) {
async () => {
console.log('Eco: Detect Smells Command Triggered');
detectSmells(contextManager);
- }
- )
+ },
+ ),
);
// Refactor Selected Smell Command
@@ -79,8 +79,8 @@ export function activate(context: vscode.ExtensionContext) {
} else {
vscode.window.showWarningMessage('Action blocked: Server is down.');
}
- }
- )
+ },
+ ),
);
// Refactor All Smells of Type Command
@@ -90,14 +90,14 @@ export function activate(context: vscode.ExtensionContext) {
async (smellId: string) => {
if (serverStatus.getStatus() === 'up') {
console.log(
- `Eco: Refactor All Smells of Type Command Triggered for ${smellId}`
+ `Eco: Refactor All Smells of Type Command Triggered for ${smellId}`,
);
refactorAllSmellsOfType(contextManager, smellId);
} else {
vscode.window.showWarningMessage('Action blocked: Server is down.');
}
- }
- )
+ },
+ ),
);
// Wipe Cache Command
@@ -107,11 +107,11 @@ export function activate(context: vscode.ExtensionContext) {
async () => {
console.log('Eco: Wipe Work Cache Command Triggered');
vscode.window.showInformationMessage(
- 'Eco: Manually wiping workspace memory... β
'
+ 'Eco: Manually wiping workspace memory... β
',
);
await wipeWorkCache(contextManager, 'manual');
- }
- )
+ },
+ ),
);
// ===============================================================
@@ -122,29 +122,29 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(
RefactorSidebarProvider.viewType,
- refactorProvider
- )
+ refactorProvider,
+ ),
);
context.subscriptions.push(
vscode.commands.registerCommand(
'ecooptimizer-vs-code-plugin.showRefactorSidebar',
- () => refactorProvider.updateView()
- )
+ () => refactorProvider.updateView(),
+ ),
);
context.subscriptions.push(
vscode.commands.registerCommand(
'ecooptimizer-vs-code-plugin.pauseRefactorSidebar',
- () => refactorProvider.pauseView()
- )
+ () => refactorProvider.pauseView(),
+ ),
);
context.subscriptions.push(
vscode.commands.registerCommand(
'ecooptimizer-vs-code-plugin.clearRefactorSidebar',
- () => refactorProvider.clearView()
- )
+ () => refactorProvider.clearView(),
+ ),
);
// ===============================================================
@@ -155,7 +155,7 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((event) => {
handleConfigurationChange(event);
- })
+ }),
);
vscode.window.onDidChangeVisibleTextEditors(async (editors) => {
@@ -168,7 +168,7 @@ export function activate(context: vscode.ExtensionContext) {
vscode.window.onDidChangeTextEditorSelection((event) => {
console.log('Eco: Detected line selection event');
lineSelectManager.commentLine(event.textEditor);
- })
+ }),
);
// Updates directory of file states (for checking if modified)
@@ -176,7 +176,7 @@ export function activate(context: vscode.ExtensionContext) {
vscode.workspace.onDidSaveTextDocument(async (document) => {
console.log('Eco: Detected document saved event');
await updateHash(contextManager, document);
- })
+ }),
);
// Handles case of documents already being open on VS Code open
@@ -191,7 +191,7 @@ export function activate(context: vscode.ExtensionContext) {
vscode.workspace.onDidOpenTextDocument(async (document) => {
console.log('Eco: Detected document opened event');
await updateHash(contextManager, document);
- })
+ }),
);
// ===============================================================
@@ -208,7 +208,7 @@ export function activate(context: vscode.ExtensionContext) {
});
}
-function showSettingsPopup() {
+function showSettingsPopup(): void {
// Check if the required settings are already configured
const config = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin');
const workspacePath = config.get('projectWorkspacePath', '');
@@ -223,31 +223,31 @@ function showSettingsPopup() {
{ modal: true },
'Continue', // Button to open settings
'Skip', // Button to dismiss
- 'Never show this again'
+ 'Never show this again',
)
.then((selection) => {
if (selection === 'Continue') {
// Open the settings page filtered to extension's settings
vscode.commands.executeCommand(
'workbench.action.openSettings',
- 'ecooptimizer'
+ 'ecooptimizer',
);
} else if (selection === 'Skip') {
// Inform user they can configure later
vscode.window.showInformationMessage(
- 'You can configure the paths later in the settings.'
+ 'You can configure the paths later in the settings.',
);
} else if (selection === 'Never show this again') {
globalData.contextManager!.setGlobalData('showSettingsPopup', false);
vscode.window.showInformationMessage(
- 'You can re-enable this popup again in the settings.'
+ 'You can re-enable this popup again in the settings.',
);
}
});
}
}
-function handleConfigurationChange(event: vscode.ConfigurationChangeEvent) {
+function handleConfigurationChange(event: vscode.ConfigurationChangeEvent): void {
// Check if any relevant setting was changed
if (
event.affectsConfiguration('ecooptimizer-vs-code-plugin.projectWorkspacePath') ||
@@ -256,12 +256,12 @@ function handleConfigurationChange(event: vscode.ConfigurationChangeEvent) {
) {
// Display a warning message about changing critical settings
vscode.window.showWarningMessage(
- 'You have changed a critical setting for the EcoOptimizer plugin. Ensure the new value is valid and correct for optimal functionality.'
+ 'You have changed a critical setting for the EcoOptimizer plugin. Ensure the new value is valid and correct for optimal functionality.',
);
}
}
-export function deactivate() {
+export function deactivate(): void {
console.log('Eco: Deactivating Plugin - Stopping Log Watching');
stopWatchingLogs();
}
diff --git a/src/types.ts b/src/types.ts
index b6e5e52..a52ddf3 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,5 +1,3 @@
-import * as vscode from 'vscode';
-
export interface Occurrence {
line: number;
endLine?: number;
diff --git a/src/ui/diffViewer.ts b/src/ui/diffViewer.ts
deleted file mode 100644
index 5adafc5..0000000
--- a/src/ui/diffViewer.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import * as vscode from 'vscode';
-import * as fs from 'fs';
-
-let statusBarItem: vscode.StatusBarItem | undefined; // Store globally
-/**
- * Displays a native VS Code diff view to compare the original and refactored code.
- * Users can accept or reject the changes.
- */
-export async function showDiffViewer(editor: vscode.TextEditor, refactoredCode: string, originalCode: string) {
- // Create temporary files for the original and refactored code
- const originalUri = vscode.Uri.file(`${editor.document.fileName}.eco-original`);
- const refactoredUri = vscode.Uri.file(`${editor.document.fileName}.eco-refactored`);
-
- // Write the original and refactored code to the temporary files
- await vscode.workspace.fs.writeFile(originalUri, Buffer.from(originalCode));
- await vscode.workspace.fs.writeFile(refactoredUri, Buffer.from(refactoredCode));
-
- // Store a reference to the original editor
- const originalEditor = editor;
-
- // Show the diff view
- await vscode.commands.executeCommand('vscode.diff', originalUri, refactoredUri, 'Eco: Code Refactor Preview');
-
- // Remove previous status bar item if it exists
- if (statusBarItem) {
- statusBarItem.dispose();
- }
-
- // Create a new status bar item
- statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
- statusBarItem.text = 'β
Accept | β Reject';
- statusBarItem.tooltip = 'Accept or reject the refactoring changes';
- statusBarItem.command = 'eco.refactor.decision';
- statusBarItem.show();
-
- // Register a command to handle the user's decision
- const disposable = vscode.commands.registerCommand('eco.refactor.decision', async () => {
- const choice = await vscode.window.showQuickPick(['Accept', 'Reject'], {
- placeHolder: 'Do you want to accept the refactoring changes?',
- });
-
- if (choice === 'Accept') {
- // Get the actual original file path (without the .eco-original suffix)
- const originalFileUri = vscode.Uri.file(editor.document.fileName);
-
- // Close the diff preview
- await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
-
- // Open the actual original file
- const document = await vscode.workspace.openTextDocument(originalFileUri);
- const originalEditor = await vscode.window.showTextDocument(document, { preview: false });
-
- // Apply the refactored code to the actual file
- await applyRefactoredCode(originalEditor, refactoredCode);
- } else {
- vscode.window.showInformationMessage('Refactoring changes rejected.');
- // Close the diff preview
- await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
- }
-
-
- // Clean up temporary files and hide the status bar item
- await vscode.workspace.fs.delete(originalUri);
- await vscode.workspace.fs.delete(refactoredUri);
- statusBarItem?.hide();
- disposable.dispose();
-});
-
-
-}
-
-/**
- * Replaces the selected code in the editor with the refactored version.
- */
-async function applyRefactoredCode(editor: vscode.TextEditor, newCode: string) {
- const fullRange = new vscode.Range(
- new vscode.Position(0, 0),
- new vscode.Position(editor.document.lineCount, 0)
- );
-
- await editor.edit((editBuilder) => {
- editBuilder.replace(fullRange, newCode);
- });
-
- vscode.window.showInformationMessage('Refactoring changes applied successfully.');
-}
\ No newline at end of file
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index a3259c3..be9fd82 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -12,7 +12,7 @@ export class FileHighlighter {
this.contextManager = contextManager;
}
- public resetHighlights() {
+ public resetHighlights(): void {
if (this.decorations.length > 0) {
console.log('Removing decorations');
this.decorations.forEach((decoration) => {
@@ -21,7 +21,7 @@ export class FileHighlighter {
}
}
- public highlightSmells(editor: vscode.TextEditor, smells: Smell[]) {
+ public highlightSmells(editor: vscode.TextEditor, smells: Smell[]): void {
this.resetHighlights();
const activeSmells = new Set(smells.map((smell) => smell.messageId));
@@ -36,12 +36,12 @@ export class FileHighlighter {
public highlightSmell(
editor: vscode.TextEditor,
smells: Smell[],
- targetSmell: string
- ) {
+ targetSmell: string,
+ ): void {
const smellLines: vscode.DecorationOptions[] = smells
.filter((smell: Smell) => {
const valid = smell.occurences.every((occurrence: { line: number }) =>
- isValidLine(occurrence.line)
+ isValidLine(occurrence.line),
);
const isCorrectType = smell.messageId === targetSmell;
return valid && isCorrectType;
@@ -65,15 +65,15 @@ export class FileHighlighter {
editor.setDecorations(this.getDecoration(colorOfSmell), smellLines);
}
- private getDecoration(color: string) {
+ private getDecoration(color: string): vscode.TextEditorDecorationType {
// ================= EXTRA DECORATIONS ===========================
- const underline = vscode.window.createTextEditorDecorationType({
- textDecoration: `wavy ${color} underline 1px`
+ const _underline = vscode.window.createTextEditorDecorationType({
+ textDecoration: `wavy ${color} underline 1px`,
});
- const flashlight = vscode.window.createTextEditorDecorationType({
+ const _flashlight = vscode.window.createTextEditorDecorationType({
isWholeLine: true,
- backgroundColor: color
+ backgroundColor: color,
});
// ================================================================
@@ -85,10 +85,10 @@ export class FileHighlighter {
contentText: 'βΆ', // Unicode right arrow
margin: '0 0 0 5px', // Space between line and arrow
color: color,
- fontWeight: 'bold'
+ fontWeight: 'bold',
},
overviewRulerColor: color,
- overviewRulerLane: vscode.OverviewRulerLane.Right
+ overviewRulerLane: vscode.OverviewRulerLane.Right,
});
const decoration = aLittleExtra; // Select decoration
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index 11275fd..af1a2dc 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
import { Smell } from '../types';
import {
refactorSelectedSmell,
- refactorAllSmellsOfType
+ refactorAllSmellsOfType,
} from '../commands/refactorSmell';
import { ContextManager } from '../context/contextManager';
@@ -12,10 +12,7 @@ export class HoverManager {
public hoverContent: vscode.MarkdownString;
private vscodeContext: vscode.ExtensionContext;
- static getInstance(
- contextManager: ContextManager,
- smells: Smell[]
- ): HoverManager {
+ static getInstance(contextManager: ContextManager, smells: Smell[]): HoverManager {
if (!HoverManager.instance) {
HoverManager.instance = new HoverManager(contextManager, smells);
} else {
@@ -24,11 +21,13 @@ export class HoverManager {
return HoverManager.instance;
}
- private constructor(private contextManager: ContextManager, smells: Smell[]) {
+ private constructor(
+ private contextManager: ContextManager,
+ smells: Smell[],
+ ) {
this.smells = smells || [];
this.vscodeContext = contextManager.context;
- this.hoverContent =
- this.registerHoverProvider() ?? new vscode.MarkdownString();
+ this.hoverContent = this.registerHoverProvider() ?? new vscode.MarkdownString();
this.registerCommands();
}
@@ -42,19 +41,19 @@ export class HoverManager {
vscode.languages.registerHoverProvider(
{ scheme: 'file', language: 'python' },
{
- provideHover: (document, position, token) => {
+ provideHover: (document, position, _token) => {
const hoverContent = this.getHoverContent(document, position);
return hoverContent ? new vscode.Hover(hoverContent) : null;
- }
- }
- )
+ },
+ },
+ ),
);
}
// hover content for detected smells
getHoverContent(
document: vscode.TextDocument,
- position: vscode.Position
+ position: vscode.Position,
): vscode.MarkdownString | null {
const lineNumber = position.line + 1; // convert to 1-based index
@@ -65,8 +64,8 @@ export class HoverManager {
occurrence.line === lineNumber ||
(occurrence.endLine &&
lineNumber >= occurrence.line &&
- lineNumber <= occurrence.endLine)
- )
+ lineNumber <= occurrence.endLine),
+ ),
);
if (smellsOnLine.length === 0) {
@@ -80,11 +79,11 @@ export class HoverManager {
hoverContent.appendMarkdown(
`**${smell.symbol}:** ${smell.message}\t\t` +
`[Refactor](command:extension.refactorThisSmell?${encodeURIComponent(
- JSON.stringify(smell)
+ JSON.stringify(smell),
)})\t\t` +
`---[Refactor all smells of this type...](command:extension.refactorAllSmellsOfType?${encodeURIComponent(
- JSON.stringify(smell)
- )})\n\n`
+ JSON.stringify(smell),
+ )})\n\n`,
);
});
@@ -99,7 +98,7 @@ export class HoverManager {
async (smell: Smell) => {
const contextManager = new ContextManager(this.vscodeContext);
await refactorSelectedSmell(contextManager, smell);
- }
+ },
),
// clicking "Refactor All Smells of this Type..."
vscode.commands.registerCommand(
@@ -107,8 +106,8 @@ export class HoverManager {
async (smell: Smell) => {
const contextManager = new ContextManager(this.vscodeContext);
await refactorAllSmellsOfType(contextManager, smell.messageId);
- }
- )
+ },
+ ),
);
}
}
diff --git a/src/ui/lineSelectionManager.ts b/src/ui/lineSelectionManager.ts
index 5e92a2f..29340ab 100644
--- a/src/ui/lineSelectionManager.ts
+++ b/src/ui/lineSelectionManager.ts
@@ -12,14 +12,14 @@ export class LineSelectionManager {
this.contextManager = contextManager;
}
- public removeLastComment() {
+ public removeLastComment(): void {
if (this.decoration) {
console.log('Removing decoration');
this.decoration.dispose();
}
}
- public commentLine(editor: vscode.TextEditor) {
+ public commentLine(editor: vscode.TextEditor): void {
this.removeLastComment();
if (!editor) {
@@ -28,7 +28,7 @@ export class LineSelectionManager {
const filePath = editor.document.fileName;
const smellsDetectRecord = this.contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!
+ envConfig.SMELL_MAP_KEY!,
)[filePath] as SmellDetectRecord;
if (!smellsDetectRecord) {
@@ -74,8 +74,8 @@ export class LineSelectionManager {
contentText: comment,
color: 'rgb(153, 211, 212)',
margin: '0 0 0 10px',
- textDecoration: 'none'
- }
+ textDecoration: 'none',
+ },
});
const selectionLine: vscode.Range[] = [];
@@ -86,7 +86,7 @@ export class LineSelectionManager {
const indexEnd = line_text.trimEnd().length + 1;
selectionLine.push(
- new vscode.Range(selectedLine, indexStart, selectedLine, indexEnd)
+ new vscode.Range(selectedLine, indexStart, selectedLine, indexEnd),
);
editor.setDecorations(this.decoration, selectionLine);
diff --git a/src/ui/refactorManager.ts b/src/ui/refactorManager.ts
deleted file mode 100644
index 67676b9..0000000
--- a/src/ui/refactorManager.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import * as vscode from "vscode";
-
-
-export class RefactorManager {
- static async previewRefactor(editor: vscode.TextEditor, refactoredCode: string) {
- try {
- // Create a new untitled document for preview
- const previewDocument = await vscode.workspace.openTextDocument({
- content: refactoredCode,
- language: "python", // Adjust this to the language of your file
- });
-
- // Show the document in a new editor column
- await vscode.window.showTextDocument(previewDocument, vscode.ViewColumn.Beside);
- } catch (error) {
- vscode.window.showErrorMessage(`Eco: Error showing refactor preview: ${error}`);
- }
- }
-}
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
index a1f2b34..c952c2c 100644
--- a/src/ui/refactorView.ts
+++ b/src/ui/refactorView.ts
@@ -17,14 +17,15 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
resolveWebviewView(
webviewView: vscode.WebviewView,
+ // eslint-disable-next-line unused-imports/no-unused-vars
context: vscode.WebviewViewResolveContext,
- _token: vscode.CancellationToken
- ) {
+ _token: vscode.CancellationToken,
+ ): void {
this._view = webviewView;
const webview = webviewView.webview;
webview.options = {
- enableScripts: true
+ enableScripts: true,
};
webview.html = this._getHtml(webview);
@@ -34,7 +35,7 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
if (webviewView.visible) {
// Use acquireVsCodeApi to get the webview state
const savedState = this._context.workspaceState.get(
- envConfig.CURRENT_REFACTOR_DATA_KEY!
+ envConfig.CURRENT_REFACTOR_DATA_KEY!,
);
if (savedState) {
@@ -57,7 +58,7 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
'vscode.diff',
vscode.Uri.file(message.original),
vscode.Uri.file(message.refactored),
- 'Refactoring Comparison'
+ 'Refactoring Comparison',
);
sidebarState.isOpening = false;
break;
@@ -73,15 +74,15 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
console.log('Initialized sidebar view');
}
- async updateView() {
+ async updateView(): Promise {
console.log('Updating view');
const refactoredData = this._context.workspaceState.get(
- envConfig.CURRENT_REFACTOR_DATA_KEY!
+ envConfig.CURRENT_REFACTOR_DATA_KEY!,
)!;
this._file_map.set(
vscode.Uri.file(refactoredData.targetFile.original),
- vscode.Uri.file(refactoredData.targetFile.refactored)
+ vscode.Uri.file(refactoredData.targetFile.refactored),
);
refactoredData.affectedFiles.forEach(({ original, refactored }) => {
@@ -93,9 +94,9 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
}
}
- private async openView(refactoredData: RefactoredData) {
+ private async openView(refactoredData: RefactoredData): Promise {
const diffView = this._context.workspaceState.get(
- envConfig.ACTIVE_DIFF_KEY!
+ envConfig.ACTIVE_DIFF_KEY!,
)!;
if (diffView.isOpen) {
@@ -104,7 +105,7 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
this._view!.webview.postMessage({
command: 'update',
data: refactoredData,
- sep: path.sep
+ sep: path.sep,
});
} else {
console.log('Gonna pause');
@@ -112,12 +113,12 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
}
}
- pauseView() {
+ async pauseView(): Promise {
console.log('pausing view');
this._view!.webview.postMessage({ command: 'pause' });
}
- async clearView() {
+ async clearView(): Promise {
await this._view?.webview.postMessage({ command: 'clear' });
this._file_map = new Map();
@@ -126,13 +127,13 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
private _getHtml(webview: vscode.Webview): string {
const scriptUri = webview.asWebviewUri(
- vscode.Uri.file(path.join(this._context.extensionPath, 'media', 'script.js'))
+ vscode.Uri.file(path.join(this._context.extensionPath, 'media', 'script.js')),
);
const customCssUri = webview.asWebviewUri(
- vscode.Uri.file(path.join(this._context.extensionPath, 'media', 'style.css'))
+ vscode.Uri.file(path.join(this._context.extensionPath, 'media', 'style.css')),
);
const vscodeCssUri = webview.asWebviewUri(
- vscode.Uri.file(path.join(this._context.extensionPath, 'media', 'vscode.css'))
+ vscode.Uri.file(path.join(this._context.extensionPath, 'media', 'vscode.css')),
);
const htmlPath = path.join(this._context.extensionPath, 'media', 'webview.html');
let htmlContent = readFileSync(htmlPath, 'utf8');
@@ -145,7 +146,7 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
return htmlContent;
}
- private async closeViews() {
+ private async closeViews(): Promise {
await this.clearView();
try {
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
@@ -153,15 +154,15 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
await this._context.workspaceState.update(
envConfig.ACTIVE_DIFF_KEY!,
- undefined
+ undefined,
);
const tempDirs =
this._context.workspaceState.get(
- envConfig.CURRENT_REFACTOR_DATA_KEY!
+ envConfig.CURRENT_REFACTOR_DATA_KEY!,
)?.tempDir ||
this._context.workspaceState.get(
- envConfig.CURRENT_REFACTOR_DATA_KEY!
+ envConfig.CURRENT_REFACTOR_DATA_KEY!,
)?.tempDirs;
if (Array.isArray(tempDirs)) {
@@ -179,11 +180,11 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
await this._context.workspaceState.update(
envConfig.CURRENT_REFACTOR_DATA_KEY!,
- undefined
+ undefined,
);
}
- private async applyRefactoring() {
+ private async applyRefactoring(): Promise {
try {
for (const [original, refactored] of this._file_map.entries()) {
const content = await vscode.workspace.fs.readFile(refactored);
diff --git a/src/utils/configManager.ts b/src/utils/configManager.ts
index fc33ffa..3874d3a 100644
--- a/src/utils/configManager.ts
+++ b/src/utils/configManager.ts
@@ -36,11 +36,11 @@ export class ConfigManager {
}
// listen for configuration changes
- static onConfigChange(callback: () => void) {
+ static onConfigChange(callback: () => void): void {
vscode.workspace.onDidChangeConfiguration((event) => {
if (
event.affectsConfiguration(
- 'ecooptimizer-vs-code-plugin.projectWorkspacePath'
+ 'ecooptimizer-vs-code-plugin.projectWorkspacePath',
) ||
event.affectsConfiguration('ecooptimizer-vs-code-plugin.logsOutputPath')
) {
@@ -50,7 +50,7 @@ export class ConfigManager {
}
// write settings to both User and Workspace if necessary
- private static writeSetting(setting: string, value: string) {
+ private static writeSetting(setting: string, value: string): void {
const config = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin');
// inspect current values in both User and Workspace settings
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts
index f60ba77..83d72ba 100644
--- a/src/utils/envConfig.ts
+++ b/src/utils/envConfig.ts
@@ -17,5 +17,5 @@ export const envConfig: EnvConfig = {
FILE_CHANGES_KEY: process.env.FILE_CHANGES_KEY,
LAST_USED_SMELLS_KEY: process.env.LAST_USED_SMELLS_KEY,
CURRENT_REFACTOR_DATA_KEY: process.env.CURRENT_REFACTOR_DATA_KEY,
- ACTIVE_DIFF_KEY: process.env.ACTIVE_DIFF_KEY
+ ACTIVE_DIFF_KEY: process.env.ACTIVE_DIFF_KEY,
};
diff --git a/src/utils/handleEditorChange.ts b/src/utils/handleEditorChange.ts
index db86c9e..6e82836 100644
--- a/src/utils/handleEditorChange.ts
+++ b/src/utils/handleEditorChange.ts
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { setTimeout } from 'timers/promises';
+import { envConfig } from './envConfig';
import { ContextManager } from '../context/contextManager';
import { ActiveDiff } from '../types';
@@ -13,12 +14,15 @@ export let sidebarState = { isOpening: false };
export async function handleEditorChanges(
contextManager: ContextManager,
- editors: readonly vscode.TextEditor[]
-) {
+ editors: readonly vscode.TextEditor[],
+): Promise {
console.log('Detected visible editor change');
- const diffState = contextManager.getWorkspaceData('activeDiff');
- const refactorData =
- contextManager.getWorkspaceData('refactorData');
+ const diffState = contextManager.getWorkspaceData(
+ envConfig.ACTIVE_DIFF_KEY!,
+ );
+ const refactorData = contextManager.getWorkspaceData(
+ envConfig.CURRENT_REFACTOR_DATA_KEY!,
+ );
if (sidebarState.isOpening) {
return;
@@ -50,16 +54,16 @@ export async function handleEditorChanges(
diffState.isOpen = false;
// console.log(`diffstate: ${diffState.isOpen}`);
// console.log(`diffstate: ${JSON.stringify(diffState)}`);
- contextManager.setWorkspaceData('activeDiff', diffState);
+ contextManager.setWorkspaceData(envConfig.ACTIVE_DIFF_KEY!, diffState);
await setTimeout(500);
- vscode.commands.executeCommand(
- 'ecooptimizer-vs-code-plugin.pauseRefactorSidebar'
- );
+ // vscode.commands.executeCommand(
+ // 'ecooptimizer-vs-code-plugin.pauseRefactorSidebar'
+ // );
return;
}
if (diffState.firstOpen) {
diffState.firstOpen = false;
- contextManager.setWorkspaceData('activeDiff', diffState);
+ contextManager.setWorkspaceData(envConfig.ACTIVE_DIFF_KEY!, diffState);
await setTimeout(500);
}
// switched from one diff editor to another, no handling needed
@@ -73,10 +77,10 @@ export async function handleEditorChanges(
// console.log(`diffstate: ${diffState.isOpen}`);
diffState.isOpen = true;
// console.log(`diffstate: ${JSON.stringify(diffState)}`);
- contextManager.setWorkspaceData('activeDiff', diffState);
+ contextManager.setWorkspaceData(envConfig.ACTIVE_DIFF_KEY!, diffState);
await setTimeout(500);
vscode.commands.executeCommand(
- 'ecooptimizer-vs-code-plugin.showRefactorSidebar'
+ 'ecooptimizer-vs-code-plugin.showRefactorSidebar',
);
}
console.log('Doing nothing');
@@ -84,8 +88,8 @@ export async function handleEditorChanges(
function isDiffEditorOpen(
editors: readonly vscode.TextEditor[],
- diffState: ActiveDiff
-) {
+ diffState: ActiveDiff,
+): boolean | undefined {
console.log('Checking if editor is a diff editor');
if (!editors.length) {
console.log('No editors found');
diff --git a/src/utils/handleSmellSettings.ts b/src/utils/handleSmellSettings.ts
index 7660b4e..f36100a 100644
--- a/src/utils/handleSmellSettings.ts
+++ b/src/utils/handleSmellSettings.ts
@@ -15,8 +15,8 @@ export function getEnabledSmells(): { [key: string]: boolean } {
*/
export function handleSmellFilterUpdate(
previousSmells: { [key: string]: boolean },
- contextManager: ContextManager
-) {
+ contextManager: ContextManager,
+): void {
const currentSmells = getEnabledSmells();
let smellsChanged = false;
@@ -26,7 +26,7 @@ export function handleSmellFilterUpdate(
vscode.window.showInformationMessage(
isEnabled
? `Eco: Enabled detection of ${formatSmellName(smell)}.`
- : `Eco: Disabled detection of ${formatSmellName(smell)}.`
+ : `Eco: Disabled detection of ${formatSmellName(smell)}.`,
);
}
});
diff --git a/src/utils/hashDocs.ts b/src/utils/hashDocs.ts
index 04fa249..3baa19c 100644
--- a/src/utils/hashDocs.ts
+++ b/src/utils/hashDocs.ts
@@ -11,11 +11,11 @@ export function hashContent(content: string): string {
// Function to update the stored hashes in workspace storage
export async function updateHash(
contextManager: ContextManager,
- document: vscode.TextDocument
-) {
+ document: vscode.TextDocument,
+): Promise {
const lastSavedHashes = contextManager.getWorkspaceData(
envConfig.FILE_CHANGES_KEY!,
- {}
+ {},
);
const lastHash = lastSavedHashes[document.fileName];
const currentHash = hashContent(document.getText());
@@ -25,7 +25,7 @@ export async function updateHash(
lastSavedHashes[document.fileName] = currentHash;
await contextManager.setWorkspaceData(
envConfig.FILE_CHANGES_KEY!,
- lastSavedHashes
+ lastSavedHashes,
);
}
}
diff --git a/src/utils/serverStatus.ts b/src/utils/serverStatus.ts
index a7bf956..804566a 100644
--- a/src/utils/serverStatus.ts
+++ b/src/utils/serverStatus.ts
@@ -1,19 +1,25 @@
import * as vscode from 'vscode';
import { EventEmitter } from 'events';
+enum ServerStatusType {
+ UNKNOWN = 'unknown',
+ UP = 'up',
+ DOWN = 'down',
+}
+
class ServerStatus extends EventEmitter {
- private status: 'unknown' | 'up' | 'down' = 'unknown';
+ private status: ServerStatusType = ServerStatusType.UNKNOWN;
- getStatus() {
+ getStatus(): ServerStatusType {
return this.status;
}
- setStatus(newStatus: 'up' | 'down') {
+ setStatus(newStatus: ServerStatusType.UP | ServerStatusType.DOWN): void {
if (this.status !== newStatus) {
- if (newStatus === 'up') {
- if (this.status !== 'unknown') {
+ if (newStatus === ServerStatusType.UP) {
+ if (this.status !== ServerStatusType.UNKNOWN) {
vscode.window.showInformationMessage(
- 'Server connection re-established. Smell detection and refactoring functionality resumed.'
+ 'Server connection re-established. Smell detection and refactoring functionality resumed.',
);
}
} else {
diff --git a/src/utils/smellDetails.ts b/src/utils/smellDetails.ts
index 897a15a..bff6a20 100644
--- a/src/utils/smellDetails.ts
+++ b/src/utils/smellDetails.ts
@@ -7,8 +7,8 @@ export const SMELL_MAP: Map = new Map([
symbol: 'use-a-generator',
message:
'Refactor to use a generator expression instead of a list comprehension inside `any()` or `all()`. This improves memory efficiency by avoiding the creation of an intermediate list.',
- colour: 'rgb(255, 204, 0)' // Yellow
- }
+ colour: 'rgb(255, 204, 0)', // Yellow
+ },
],
[
'R0913', // id: "too-many-arguments"
@@ -16,8 +16,8 @@ export const SMELL_MAP: Map = new Map([
symbol: 'too-many-arguments',
message:
'Refactor the function to reduce the number of parameters. Functions with too many arguments can become difficult to maintain and understand. Consider breaking it into smaller, more manageable functions.',
- colour: 'rgb(255, 102, 102)' // Light Red
- }
+ colour: 'rgb(255, 102, 102)', // Light Red
+ },
],
[
'R6301', // id: "no-self-use"
@@ -25,8 +25,8 @@ export const SMELL_MAP: Map = new Map([
symbol: 'no-self-use',
message:
"Refactor the method to make it static, as it does not use `self`. Static methods do not require an instance and improve clarity and performance when the method doesn't depend on instance data.",
- colour: 'rgb(204, 255, 255)' // Light Cyan
- }
+ colour: 'rgb(204, 255, 255)', // Light Cyan
+ },
],
[
'LLE001', // id: "long-lambda-expression"
@@ -34,8 +34,8 @@ export const SMELL_MAP: Map = new Map([
symbol: 'long-lambda-expression',
message:
'Refactor the lambda expression to improve readability. Long lambda expressions can be confusing; breaking them into named functions can make the code more understandable and maintainable.',
- colour: 'rgb(153, 102, 255)' // Light Purple
- }
+ colour: 'rgb(153, 102, 255)', // Light Purple
+ },
],
[
'LMC001', // id: "long-message-chain"
@@ -43,8 +43,8 @@ export const SMELL_MAP: Map = new Map([
symbol: 'long-message-chain',
message:
'Refactor the message chain to improve readability and performance. Long chains of method calls can be hard to follow and may impact performance. Consider breaking them into smaller steps.',
- colour: 'rgb(255, 204, 255)' // Light Pink
- }
+ colour: 'rgb(255, 204, 255)', // Light Pink
+ },
],
[
'UVA001', // id: "unused_variables_and_attributes"
@@ -52,8 +52,8 @@ export const SMELL_MAP: Map = new Map([
symbol: 'unused-variables-and-attributes',
message:
'Remove unused variables or attributes to clean up the code. Keeping unused elements in the code increases its complexity without providing any benefit, making it harder to maintain.',
- colour: 'rgb(255, 255, 102)' // Light Yellow
- }
+ colour: 'rgb(255, 255, 102)', // Light Yellow
+ },
],
[
'LEC001', // id: "long-element-chain"
@@ -61,8 +61,8 @@ export const SMELL_MAP: Map = new Map([
symbol: 'long-element-chain',
message:
'Refactor the long element chain for better performance and clarity. Chains of nested elements are harder to read and can lead to inefficiency, especially when accessing deep levels repeatedly.',
- colour: 'rgb(204, 204, 255)' // Light Blue
- }
+ colour: 'rgb(204, 204, 255)', // Light Blue
+ },
],
[
'CRC001', // id: "cached-repeated-calls"
@@ -70,8 +70,8 @@ export const SMELL_MAP: Map = new Map([
symbol: 'cached-repeated-calls',
message:
'Refactor by caching repeated function calls to improve performance. Repeated calls to the same function can be avoided by storing the result, which saves processing time and enhances performance.',
- colour: 'rgb(102, 255, 102)' // Light Green
- }
+ colour: 'rgb(102, 255, 102)', // Light Green
+ },
],
[
'SCL001', // id: "string-concat-loop"
@@ -79,7 +79,7 @@ export const SMELL_MAP: Map = new Map([
symbol: 'string-concat-loop',
message:
'Refactor to use list accumulation instead of string concatenation inside a loop. Concatenating strings in a loop is inefficient; list accumulation and joining are faster and use less memory.',
- colour: 'rgb(255, 178, 102)' // Light Orange
- }
- ]
+ colour: 'rgb(255, 178, 102)', // Light Orange
+ },
+ ],
]);
From 0c3d60f4fd5ff29cfea2e2817dd73146d9b57efa Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 22 Feb 2025 15:20:28 -0500
Subject: [PATCH 49/83] linter ignores type def files + stop checking line
ending type
---
.prettierignore | 1 +
.prettierrc | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/.prettierignore b/.prettierignore
index e3aad94..db2facf 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -6,3 +6,4 @@ media
.env
**/*.config.js
**/*.mjs'
+**/*d.ts
diff --git a/.prettierrc b/.prettierrc
index 69014df..ed1cfa5 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,6 +1,5 @@
{
"bracketSpacing": true,
- "endOfLine": "lf",
"printWidth": 85,
"semi": true,
"singleQuote": true,
From 0617d35b64880c59b05fe1aecc4f3e25a1ee717f Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sun, 23 Feb 2025 20:00:49 -0500
Subject: [PATCH 50/83] minor bug fixes
---
src/api/backend.ts | 7 ++++---
src/commands/detectSmells.ts | 6 +++---
src/commands/refactorSmell.ts | 8 ++++----
src/commands/showLogs.ts | 6 +++---
src/utils/serverStatus.ts | 2 +-
5 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 2891f93..23e9785 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -3,6 +3,7 @@ import * as vscode from 'vscode';
import { Smell } from '../types';
import { envConfig } from '../utils/envConfig';
import { serverStatus } from '../utils/serverStatus';
+import { ServerStatusType } from '../utils/serverStatus';
const BASE_URL = `http://${envConfig.SERVER_URL}`; // API URL for Python backend
@@ -10,12 +11,12 @@ export async function checkServerStatus(): Promise {
try {
const response = await fetch('http://localhost:8000/health');
if (response.ok) {
- serverStatus.setStatus('up');
+ serverStatus.setStatus(ServerStatusType.UP);
} else {
- serverStatus.setStatus('down');
+ serverStatus.setStatus(ServerStatusType.DOWN);
}
} catch {
- serverStatus.setStatus('down');
+ serverStatus.setStatus(ServerStatusType.DOWN);
}
}
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 82bf030..4049007 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -8,13 +8,13 @@ import { envConfig } from '../utils/envConfig';
import { hashContent, updateHash } from '../utils/hashDocs';
import { wipeWorkCache } from './wipeWorkCache'; // β
Import cache wipe function
import { Smell } from '../types';
-import { serverStatus } from '../utils/serverStatus';
+import { serverStatus, ServerStatusType } from '../utils/serverStatus';
let serverOn: boolean = true;
-serverStatus.on('change', (newStatus) => {
+serverStatus.on('change', (newStatus: ServerStatusType) => {
console.log('Server status changed:', newStatus);
- if (newStatus === 'down') {
+ if (newStatus === ServerStatusType.DOWN) {
serverOn = false;
vscode.window.showWarningMessage(
'Smell detection limited. Only cached smells will be shown.',
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 6a839c9..22b6d89 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -9,13 +9,13 @@ import { sidebarState } from '../utils/handleEditorChange';
import { FileHighlighter } from '../ui/fileHighlighter';
import { ContextManager } from '../context/contextManager';
-import { RefactorManager } from '../ui/refactorManager';
import { setTimeout } from 'timers/promises';
import { serverStatus } from '../utils/serverStatus';
+import { ServerStatusType } from '../utils/serverStatus';
-serverStatus.on('change', (newStatus) => {
+serverStatus.on('change', (newStatus: ServerStatusType) => {
console.log('Server status changed:', newStatus);
- if (newStatus === 'down') {
+ if (newStatus === ServerStatusType.DOWN) {
vscode.window.showWarningMessage('No refactoring is possible at this time.');
}
});
@@ -247,7 +247,7 @@ export async function refactorAllSmellsOfType(
// startRefactoringSession(contextManager,editor,combinedRefactoredData);
if (combinedRefactoredData) {
- await RefactorManager.previewRefactor(editor, combinedRefactoredData);
+ // await RefactorManager.previewRefactor(editor, combinedRefactoredData);
vscode.window.showInformationMessage(
`Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(
4,
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index bb065eb..efc7ce6 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -3,7 +3,7 @@ import WebSocket from 'ws';
import { initLogs } from '../api/backend';
import { envConfig } from '../utils/envConfig';
-import { serverStatus } from '../utils/serverStatus';
+import { serverStatus, ServerStatusType } from '../utils/serverStatus';
import { globalData } from '../extension';
class LogInitializationError extends Error {
@@ -30,9 +30,9 @@ let refactorSmellChannel: vscode.OutputChannel | undefined;
let CHANNELS_CREATED = false;
-serverStatus.on('change', async (newStatus) => {
+serverStatus.on('change', async (newStatus: ServerStatusType) => {
console.log('Server status changed:', newStatus);
- if (newStatus === 'down') {
+ if (newStatus === ServerStatusType.DOWN) {
mainLogChannel?.appendLine('Server connection lost');
} else {
mainLogChannel?.appendLine('Server connection re-established.');
diff --git a/src/utils/serverStatus.ts b/src/utils/serverStatus.ts
index 804566a..80aa45e 100644
--- a/src/utils/serverStatus.ts
+++ b/src/utils/serverStatus.ts
@@ -1,7 +1,7 @@
import * as vscode from 'vscode';
import { EventEmitter } from 'events';
-enum ServerStatusType {
+export enum ServerStatusType {
UNKNOWN = 'unknown',
UP = 'up',
DOWN = 'down',
From bf4839a7877f19f9d08fcabc4d618cd7c28632f6 Mon Sep 17 00:00:00 2001
From: tbrar06
Date: Sun, 23 Feb 2025 20:29:53 -0500
Subject: [PATCH 51/83] ssm-lab/capstone--source-code-optimizer#385 Added test
libraries, set up initial structure for unit tests
---
package-lock.json | 572 ++++++++++++++----
package.json | 5 +
.../commands/testWipeWorkCache.ts | 0
3 files changed, 445 insertions(+), 132 deletions(-)
rename src/test/extension.test.ts => tests/commands/testWipeWorkCache.ts (100%)
diff --git a/package-lock.json b/package-lock.json
index df04607..588e0d0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,14 +16,17 @@
"ws": "^8.18.0"
},
"devDependencies": {
+ "@types/chai": "^5.0.1",
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
+ "@types/sinon": "^17.0.4",
"@types/vscode": "^1.92.0",
"@types/ws": "^8.5.14",
"@typescript-eslint/eslint-plugin": "^8.24.1",
"@typescript-eslint/parser": "^8.24.1",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
+ "chai": "^5.2.0",
"css-loader": "^7.1.2",
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.0.1",
@@ -31,8 +34,10 @@
"eslint-plugin-unused-imports": "^4.1.4",
"husky": "^9.1.7",
"lint-staged": "^15.4.3",
+ "mocha": "^11.1.0",
"prettier": "^3.5.2",
"prettier-plugin-tailwindcss": "^0.6.11",
+ "sinon": "^19.0.2",
"style-loader": "^4.0.0",
"ts-loader": "^9.5.1",
"typescript": "^5.7.2",
@@ -422,6 +427,72 @@
"url": "https://opencollective.com/unts"
}
},
+ "node_modules/@sinonjs/commons": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "node_modules/@sinonjs/fake-timers": {
+ "version": "13.0.5",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz",
+ "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@sinonjs/commons": "^3.0.1"
+ }
+ },
+ "node_modules/@sinonjs/samsam": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz",
+ "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@sinonjs/commons": "^3.0.1",
+ "lodash.get": "^4.4.2",
+ "type-detect": "^4.1.0"
+ }
+ },
+ "node_modules/@sinonjs/samsam/node_modules/type-detect": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz",
+ "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@sinonjs/text-encoding": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz",
+ "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==",
+ "dev": true,
+ "license": "(Unlicense OR Apache-2.0)"
+ },
+ "node_modules/@types/chai": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.0.1.tgz",
+ "integrity": "sha512-5T8ajsg3M/FOncpLYW7sdOcD6yf4+722sze/tc4KQV0P8Z2rAr3SAuHCIkYmYpt8VbcQlnz8SxlOlPQYefe4cA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/dotenv": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
@@ -486,6 +557,23 @@
"undici-types": "~6.19.2"
}
},
+ "node_modules/@types/sinon": {
+ "version": "17.0.4",
+ "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz",
+ "integrity": "sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/sinonjs__fake-timers": "*"
+ }
+ },
+ "node_modules/@types/sinonjs__fake-timers": {
+ "version": "8.1.5",
+ "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz",
+ "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/vscode": {
"version": "1.96.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz",
@@ -710,6 +798,196 @@
"node": ">=18"
}
},
+ "node_modules/@vscode/test-cli/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/@vscode/test-cli/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha": {
+ "version": "10.8.2",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz",
+ "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-colors": "^4.1.3",
+ "browser-stdout": "^1.3.1",
+ "chokidar": "^3.5.3",
+ "debug": "^4.3.5",
+ "diff": "^5.2.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-up": "^5.0.0",
+ "glob": "^8.1.0",
+ "he": "^1.2.0",
+ "js-yaml": "^4.1.0",
+ "log-symbols": "^4.1.0",
+ "minimatch": "^5.1.6",
+ "ms": "^2.1.3",
+ "serialize-javascript": "^6.0.2",
+ "strip-json-comments": "^3.1.1",
+ "supports-color": "^8.1.1",
+ "workerpool": "^6.5.1",
+ "yargs": "^16.2.0",
+ "yargs-parser": "^20.2.9",
+ "yargs-unparser": "^2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha.js"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/supports-color": {
+ "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"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@vscode/test-cli/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",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/@vscode/test-electron": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz",
@@ -1102,6 +1380,16 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1343,6 +1631,23 @@
],
"license": "CC-BY-4.0"
},
+ "node_modules/chai": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz",
+ "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assertion-error": "^2.0.1",
+ "check-error": "^2.1.1",
+ "deep-eql": "^5.0.1",
+ "loupe": "^3.1.0",
+ "pathval": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -1373,6 +1678,16 @@
"node": ">=8"
}
},
+ "node_modules/check-error": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+ "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ }
+ },
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -1715,6 +2030,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/deep-eql": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -3014,6 +3339,13 @@
"setimmediate": "^1.0.5"
}
},
+ "node_modules/just-extend": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz",
+ "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -3212,6 +3544,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
+ "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -3384,6 +3724,13 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/loupe": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz",
+ "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
@@ -3506,9 +3853,9 @@
}
},
"node_modules/mocha": {
- "version": "10.8.2",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz",
- "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==",
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz",
+ "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3519,7 +3866,7 @@
"diff": "^5.2.0",
"escape-string-regexp": "^4.0.0",
"find-up": "^5.0.0",
- "glob": "^8.1.0",
+ "glob": "^10.4.5",
"he": "^1.2.0",
"js-yaml": "^4.1.0",
"log-symbols": "^4.1.0",
@@ -3529,8 +3876,8 @@
"strip-json-comments": "^3.1.1",
"supports-color": "^8.1.1",
"workerpool": "^6.5.1",
- "yargs": "^16.2.0",
- "yargs-parser": "^20.2.9",
+ "yargs": "^17.7.2",
+ "yargs-parser": "^21.1.1",
"yargs-unparser": "^2.0.0"
},
"bin": {
@@ -3538,57 +3885,7 @@
"mocha": "bin/mocha.js"
},
"engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/mocha/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/mocha/node_modules/cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
- }
- },
- "node_modules/mocha/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==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/mocha/node_modules/glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/mocha/node_modules/minimatch": {
@@ -3604,34 +3901,6 @@
"node": ">=10"
}
},
- "node_modules/mocha/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/mocha/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/mocha/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@@ -3648,53 +3917,6 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "node_modules/mocha/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",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/mocha/node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/mocha/node_modules/yargs-parser": {
- "version": "20.2.9",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -3733,6 +3955,20 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"license": "MIT"
},
+ "node_modules/nise": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz",
+ "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@sinonjs/commons": "^3.0.1",
+ "@sinonjs/fake-timers": "^13.0.1",
+ "@sinonjs/text-encoding": "^0.7.3",
+ "just-extend": "^6.2.0",
+ "path-to-regexp": "^8.1.0"
+ }
+ },
"node_modules/node-gyp-build": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
@@ -4044,6 +4280,26 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/path-to-regexp": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
+ "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/pathval": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
+ "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.16"
+ }
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -4689,6 +4945,48 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/sinon": {
+ "version": "19.0.2",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.2.tgz",
+ "integrity": "sha512-euuToqM+PjO4UgXeLETsfQiuoyPXlqFezr6YZDFwHR3t4qaX0fZUe1MfPMznTL5f8BWrVS89KduLdMUsxFCO6g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@sinonjs/commons": "^3.0.1",
+ "@sinonjs/fake-timers": "^13.0.2",
+ "@sinonjs/samsam": "^8.0.1",
+ "diff": "^7.0.0",
+ "nise": "^6.1.1",
+ "supports-color": "^7.2.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/sinon"
+ }
+ },
+ "node_modules/sinon/node_modules/diff": {
+ "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"
+ }
+ },
+ "node_modules/sinon/node_modules/supports-color": {
+ "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"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/slice-ansi": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
@@ -5229,6 +5527,16 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/typescript": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
diff --git a/package.json b/package.json
index ab07abe..d2854ba 100644
--- a/package.json
+++ b/package.json
@@ -177,14 +177,17 @@
]
},
"devDependencies": {
+ "@types/chai": "^5.0.1",
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
+ "@types/sinon": "^17.0.4",
"@types/vscode": "^1.92.0",
"@types/ws": "^8.5.14",
"@typescript-eslint/eslint-plugin": "^8.24.1",
"@typescript-eslint/parser": "^8.24.1",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
+ "chai": "^5.2.0",
"css-loader": "^7.1.2",
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.0.1",
@@ -192,8 +195,10 @@
"eslint-plugin-unused-imports": "^4.1.4",
"husky": "^9.1.7",
"lint-staged": "^15.4.3",
+ "mocha": "^11.1.0",
"prettier": "^3.5.2",
"prettier-plugin-tailwindcss": "^0.6.11",
+ "sinon": "^19.0.2",
"style-loader": "^4.0.0",
"ts-loader": "^9.5.1",
"typescript": "^5.7.2",
diff --git a/src/test/extension.test.ts b/tests/commands/testWipeWorkCache.ts
similarity index 100%
rename from src/test/extension.test.ts
rename to tests/commands/testWipeWorkCache.ts
From 0fb9fc55e5994c7e9ea31db67c9df0aac73f6d76 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 1 Mar 2025 00:47:19 -0500
Subject: [PATCH 52/83] setup: configured project for jest testing
ssm-lab/capstone--source-code-optimizer#376
---
package-lock.json | 7154 ++++++++++++++++++++++-----------
package.json | 27 +-
src/utils/envConfig.ts | 2 +-
test/mocks/env-config-mock.ts | 10 +
test/mocks/vscode-mock.ts | 71 +
test/setup.ts | 3 +
tsconfig.json | 32 +-
7 files changed, 5010 insertions(+), 2289 deletions(-)
create mode 100644 test/mocks/env-config-mock.ts
create mode 100644 test/mocks/vscode-mock.ts
create mode 100644 test/setup.ts
diff --git a/package-lock.json b/package-lock.json
index 588e0d0..4374e89 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,29 +16,26 @@
"ws": "^8.18.0"
},
"devDependencies": {
- "@types/chai": "^5.0.1",
- "@types/mocha": "^10.0.10",
+ "@types/jest": "^29.5.14",
"@types/node": "20.x",
- "@types/sinon": "^17.0.4",
"@types/vscode": "^1.92.0",
"@types/ws": "^8.5.14",
"@typescript-eslint/eslint-plugin": "^8.24.1",
"@typescript-eslint/parser": "^8.24.1",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
- "chai": "^5.2.0",
"css-loader": "^7.1.2",
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-unused-imports": "^4.1.4",
"husky": "^9.1.7",
+ "jest": "^29.7.0",
"lint-staged": "^15.4.3",
- "mocha": "^11.1.0",
"prettier": "^3.5.2",
"prettier-plugin-tailwindcss": "^0.6.11",
- "sinon": "^19.0.2",
"style-loader": "^4.0.0",
+ "ts-jest": "^29.2.6",
"ts-loader": "^9.5.1",
"typescript": "^5.7.2",
"webpack": "^5.95.0",
@@ -48,6 +45,501 @@
"vscode": "^1.92.0"
}
},
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.26.8",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz",
+ "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.26.9",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz",
+ "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.26.2",
+ "@babel/generator": "^7.26.9",
+ "@babel/helper-compilation-targets": "^7.26.5",
+ "@babel/helper-module-transforms": "^7.26.0",
+ "@babel/helpers": "^7.26.9",
+ "@babel/parser": "^7.26.9",
+ "@babel/template": "^7.26.9",
+ "@babel/traverse": "^7.26.9",
+ "@babel/types": "^7.26.9",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.26.9",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz",
+ "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.26.9",
+ "@babel/types": "^7.26.9",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.26.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz",
+ "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.26.5",
+ "@babel/helper-validator-option": "^7.25.9",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
+ "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
+ "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.26.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
+ "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
+ "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.26.9",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz",
+ "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.26.9",
+ "@babel/types": "^7.26.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.26.9",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz",
+ "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.26.9"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-bigint": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-static-block": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
+ "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz",
+ "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-meta": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz",
+ "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-private-property-in-object": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+ "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz",
+ "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.26.9",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
+ "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/parser": "^7.26.9",
+ "@babel/types": "^7.26.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.26.9",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz",
+ "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/generator": "^7.26.9",
+ "@babel/parser": "^7.26.9",
+ "@babel/template": "^7.26.9",
+ "@babel/types": "^7.26.9",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.26.9",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
+ "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@bcoe/v8-coverage": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
@@ -55,6 +547,32 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@@ -301,2961 +819,4787 @@
"node": ">=12"
}
},
- "node_modules/@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "node_modules/@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
"dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
- "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
- "license": "MIT",
"dependencies": {
- "@jridgewell/set-array": "^1.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.24"
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
},
"engines": {
- "node": ">=6.0.0"
+ "node": ">=8"
}
},
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
}
},
- "node_modules/@jridgewell/set-array": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "license": "MIT",
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
"engines": {
- "node": ">=6.0.0"
+ "node": ">=6"
}
},
- "node_modules/@jridgewell/source-map": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
- "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
- "license": "MIT",
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
"dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25"
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
- "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
- "license": "MIT"
- },
- "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==",
- "license": "MIT",
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
"dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
}
},
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
+ "p-locate": "^4.1.0"
},
"engines": {
- "node": ">= 8"
+ "node": ">=8"
}
},
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
"engines": {
- "node": ">= 8"
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
+ "p-limit": "^2.2.0"
},
"engines": {
- "node": ">= 8"
+ "node": ">=8"
}
},
- "node_modules/@pkgjs/parseargs": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
- "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"dev": true,
- "license": "MIT",
- "optional": true,
"engines": {
- "node": ">=14"
+ "node": ">=8"
}
},
- "node_modules/@pkgr/core": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
- "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "node_modules/@istanbuljs/schema": {
+ "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": "^12.20.0 || ^14.18.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/unts"
+ "node": ">=8"
}
},
- "node_modules/@sinonjs/commons": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
- "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+ "node_modules/@jest/console": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
+ "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
"dev": true,
- "license": "BSD-3-Clause",
"dependencies": {
- "type-detect": "4.0.8"
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@sinonjs/fake-timers": {
- "version": "13.0.5",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz",
- "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==",
+ "node_modules/@jest/core": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
+ "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
"dev": true,
- "license": "BSD-3-Clause",
"dependencies": {
- "@sinonjs/commons": "^3.0.1"
+ "@jest/console": "^29.7.0",
+ "@jest/reporters": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.9",
+ "jest-changed-files": "^29.7.0",
+ "jest-config": "^29.7.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-resolve-dependencies": "^29.7.0",
+ "jest-runner": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "jest-watcher": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
}
},
- "node_modules/@sinonjs/samsam": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz",
- "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==",
+ "node_modules/@jest/core/node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
"dev": true,
- "license": "BSD-3-Clause",
"dependencies": {
- "@sinonjs/commons": "^3.0.1",
- "lodash.get": "^4.4.2",
- "type-detect": "^4.1.0"
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@sinonjs/samsam/node_modules/type-detect": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz",
- "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==",
+ "node_modules/@jest/core/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
- "license": "MIT",
"engines": {
- "node": ">=4"
+ "node": ">=8"
}
},
- "node_modules/@sinonjs/text-encoding": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz",
- "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==",
- "dev": true,
- "license": "(Unlicense OR Apache-2.0)"
- },
- "node_modules/@types/chai": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.0.1.tgz",
- "integrity": "sha512-5T8ajsg3M/FOncpLYW7sdOcD6yf4+722sze/tc4KQV0P8Z2rAr3SAuHCIkYmYpt8VbcQlnz8SxlOlPQYefe4cA==",
+ "node_modules/@jest/core/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@types/deep-eql": "*"
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "node_modules/@types/deep-eql": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
- "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "node_modules/@jest/environment": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
+ "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
"dev": true,
- "license": "MIT"
- },
- "node_modules/@types/dotenv": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
- "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==",
- "license": "MIT",
"dependencies": {
- "@types/node": "*"
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "jest-mock": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@types/eslint": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
- "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
- "license": "MIT",
+ "node_modules/@jest/expect": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
+ "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
+ "dev": true,
"dependencies": {
- "@types/estree": "*",
- "@types/json-schema": "*"
+ "expect": "^29.7.0",
+ "jest-snapshot": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@types/eslint-scope": {
- "version": "3.7.7",
- "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
- "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
- "license": "MIT",
+ "node_modules/@jest/expect-utils": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
+ "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
+ "dev": true,
"dependencies": {
- "@types/eslint": "*",
- "@types/estree": "*"
+ "jest-get-type": "^29.6.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
- "license": "MIT"
- },
- "node_modules/@types/istanbul-lib-coverage": {
- "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==",
+ "node_modules/@jest/fake-timers": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
+ "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
"dev": true,
- "license": "MIT"
- },
- "node_modules/@types/json-schema": {
- "version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "license": "MIT"
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@sinonjs/fake-timers": "^10.0.2",
+ "@types/node": "*",
+ "jest-message-util": "^29.7.0",
+ "jest-mock": "^29.7.0",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
},
- "node_modules/@types/mocha": {
- "version": "10.0.10",
- "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz",
- "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==",
+ "node_modules/@jest/fake-timers/node_modules/@sinonjs/fake-timers": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
+ "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
"dev": true,
- "license": "MIT"
- },
- "node_modules/@types/node": {
- "version": "20.17.14",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz",
- "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==",
- "license": "MIT",
"dependencies": {
- "undici-types": "~6.19.2"
+ "@sinonjs/commons": "^3.0.0"
}
},
- "node_modules/@types/sinon": {
- "version": "17.0.4",
- "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz",
- "integrity": "sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==",
+ "node_modules/@jest/globals": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
+ "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@types/sinonjs__fake-timers": "*"
+ "@jest/environment": "^29.7.0",
+ "@jest/expect": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "jest-mock": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@types/sinonjs__fake-timers": {
- "version": "8.1.5",
- "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz",
- "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==",
+ "node_modules/@jest/reporters": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
+ "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
"dev": true,
- "license": "MIT"
- },
- "node_modules/@types/vscode": {
- "version": "1.96.0",
- "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz",
- "integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==",
+ "dependencies": {
+ "@bcoe/v8-coverage": "^0.2.3",
+ "@jest/console": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "exit": "^0.1.2",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-instrument": "^6.0.0",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.1.3",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "slash": "^3.0.0",
+ "string-length": "^4.0.1",
+ "strip-ansi": "^6.0.0",
+ "v8-to-istanbul": "^9.0.1"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/reporters/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
- "license": "MIT"
+ "engines": {
+ "node": ">=8"
+ }
},
- "node_modules/@types/ws": {
- "version": "8.5.14",
- "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz",
- "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==",
+ "node_modules/@jest/reporters/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
- "@types/node": "*"
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.24.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz",
- "integrity": "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==",
+ "node_modules/@jest/reporters/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.24.1",
- "@typescript-eslint/type-utils": "8.24.1",
- "@typescript-eslint/utils": "8.24.1",
- "@typescript-eslint/visitor-keys": "8.24.1",
- "graphemer": "^1.4.0",
- "ignore": "^5.3.1",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.0.1"
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "*"
},
"funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/@typescript-eslint/parser": {
- "version": "8.24.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.1.tgz",
- "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==",
+ "node_modules/@jest/reporters/node_modules/jest-worker": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
"dev": true,
"dependencies": {
- "@typescript-eslint/scope-manager": "8.24.1",
- "@typescript-eslint/types": "8.24.1",
- "@typescript-eslint/typescript-estree": "8.24.1",
- "@typescript-eslint/visitor-keys": "8.24.1",
- "debug": "^4.3.4"
+ "@types/node": "*",
+ "jest-util": "^29.7.0",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.24.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz",
- "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==",
+ "node_modules/@jest/reporters/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "8.24.1",
- "@typescript-eslint/visitor-keys": "8.24.1"
+ "brace-expansion": "^1.1.7"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
+ "node": "*"
}
},
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.24.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.1.tgz",
- "integrity": "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==",
+ "node_modules/@jest/reporters/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.24.1",
- "@typescript-eslint/utils": "8.24.1",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.0.1"
+ "ansi-regex": "^5.0.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "node": ">=8"
}
},
- "node_modules/@typescript-eslint/types": {
- "version": "8.24.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz",
- "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==",
+ "node_modules/@jest/reporters/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": ">=10"
},
"funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.24.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz",
- "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==",
+ "node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "8.24.1",
- "@typescript-eslint/visitor-keys": "8.24.1",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.0.1"
+ "@sinclair/typebox": "^0.27.8"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <5.8.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@typescript-eslint/utils": {
- "version": "8.24.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz",
- "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==",
+ "node_modules/@jest/source-map": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
+ "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
"dev": true,
"dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.24.1",
- "@typescript-eslint/types": "8.24.1",
- "@typescript-eslint/typescript-estree": "8.24.1"
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "callsites": "^3.0.0",
+ "graceful-fs": "^4.2.9"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.24.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz",
- "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==",
+ "node_modules/@jest/test-result": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
+ "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "8.24.1",
- "eslint-visitor-keys": "^4.2.0"
+ "@jest/console": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "collect-v8-coverage": "^1.0.0"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
- "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "node_modules/@jest/test-sequencer": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
+ "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
"dev": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "dependencies": {
+ "@jest/test-result": "^29.7.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "slash": "^3.0.0"
},
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@vscode/test-cli": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.10.tgz",
- "integrity": "sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==",
+ "node_modules/@jest/transform": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
+ "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@types/mocha": "^10.0.2",
- "c8": "^9.1.0",
- "chokidar": "^3.5.3",
- "enhanced-resolve": "^5.15.0",
- "glob": "^10.3.10",
- "minimatch": "^9.0.3",
- "mocha": "^10.2.0",
- "supports-color": "^9.4.0",
- "yargs": "^17.7.2"
- },
- "bin": {
- "vscode-test": "out/bin.mjs"
+ "@babel/core": "^7.11.6",
+ "@jest/types": "^29.6.3",
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "babel-plugin-istanbul": "^6.1.1",
+ "chalk": "^4.0.0",
+ "convert-source-map": "^2.0.0",
+ "fast-json-stable-stringify": "^2.1.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "pirates": "^4.0.4",
+ "slash": "^3.0.0",
+ "write-file-atomic": "^4.0.2"
},
"engines": {
- "node": ">=18"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@vscode/test-cli/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "node_modules/@jest/types": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
+ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^17.0.8",
+ "chalk": "^4.0.0"
+ },
"engines": {
- "node": ">=8"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@vscode/test-cli/node_modules/cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
- "dev": true,
- "license": "ISC",
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "license": "MIT",
"dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
- "node_modules/@vscode/test-cli/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==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@vscode/test-cli/node_modules/mocha": {
- "version": "10.8.2",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz",
- "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==",
- "dev": true,
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"license": "MIT",
- "dependencies": {
- "ansi-colors": "^4.1.3",
- "browser-stdout": "^1.3.1",
- "chokidar": "^3.5.3",
- "debug": "^4.3.5",
- "diff": "^5.2.0",
- "escape-string-regexp": "^4.0.0",
- "find-up": "^5.0.0",
- "glob": "^8.1.0",
- "he": "^1.2.0",
- "js-yaml": "^4.1.0",
- "log-symbols": "^4.1.0",
- "minimatch": "^5.1.6",
- "ms": "^2.1.3",
- "serialize-javascript": "^6.0.2",
- "strip-json-comments": "^3.1.1",
- "supports-color": "^8.1.1",
- "workerpool": "^6.5.1",
- "yargs": "^16.2.0",
- "yargs-parser": "^20.2.9",
- "yargs-unparser": "^2.0.0"
- },
- "bin": {
- "_mocha": "bin/_mocha",
- "mocha": "bin/mocha.js"
- },
"engines": {
- "node": ">= 14.0.0"
+ "node": ">=6.0.0"
}
},
- "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
- },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "license": "MIT",
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "node": ">=6.0.0"
}
},
- "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "dev": true,
- "license": "ISC",
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "license": "MIT",
"dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=10"
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
}
},
- "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "license": "MIT"
+ },
+ "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==",
"license": "MIT",
"dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
}
},
- "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
},
"engines": {
- "node": ">=10"
+ "node": ">= 8"
}
},
- "node_modules/@vscode/test-cli/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
"engines": {
- "node": ">=8"
+ "node": ">= 8"
}
},
- "node_modules/@vscode/test-cli/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "ansi-regex": "^5.0.1"
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
},
"engines": {
- "node": ">=8"
+ "node": ">= 8"
}
},
- "node_modules/@vscode/test-cli/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==",
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
+ "optional": true,
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ "node": ">=14"
}
},
- "node_modules/@vscode/test-cli/node_modules/yargs-parser": {
- "version": "20.2.9",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "node_modules/@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
"dev": true,
- "license": "ISC",
"engines": {
- "node": ">=10"
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
}
},
- "node_modules/@vscode/test-electron": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz",
- "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==",
+ "node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+ "dev": true
+ },
+ "node_modules/@sinonjs/commons": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
"dev": true,
- "license": "MIT",
+ "license": "BSD-3-Clause",
"dependencies": {
- "http-proxy-agent": "^7.0.2",
- "https-proxy-agent": "^7.0.5",
- "jszip": "^3.10.1",
- "ora": "^7.0.1",
- "semver": "^7.6.2"
- },
- "engines": {
- "node": ">=16"
+ "type-detect": "4.0.8"
}
},
- "node_modules/@webassemblyjs/ast": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
- "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
- "license": "MIT",
- "dependencies": {
- "@webassemblyjs/helper-numbers": "1.13.2",
- "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
- }
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
+ "dev": true,
+ "optional": true,
+ "peer": true
},
- "node_modules/@webassemblyjs/floating-point-hex-parser": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
- "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
- "license": "MIT"
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true,
+ "optional": true,
+ "peer": true
},
- "node_modules/@webassemblyjs/helper-api-error": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
- "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
- "license": "MIT"
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true,
+ "optional": true,
+ "peer": true
},
- "node_modules/@webassemblyjs/helper-buffer": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
- "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
- "license": "MIT"
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
},
- "node_modules/@webassemblyjs/helper-numbers": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
- "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
- "license": "MIT",
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
"dependencies": {
- "@webassemblyjs/floating-point-hex-parser": "1.13.2",
- "@webassemblyjs/helper-api-error": "1.13.2",
- "@xtuc/long": "4.2.2"
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
}
},
- "node_modules/@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
- "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
- "license": "MIT"
- },
- "node_modules/@webassemblyjs/helper-wasm-section": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
- "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
+ "node_modules/@types/babel__generator": {
+ "version": "7.6.8",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
+ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz",
+ "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "node_modules/@types/dotenv": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
+ "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==",
"license": "MIT",
"dependencies": {
- "@webassemblyjs/ast": "1.14.1",
- "@webassemblyjs/helper-buffer": "1.14.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
- "@webassemblyjs/wasm-gen": "1.14.1"
+ "@types/node": "*"
}
},
- "node_modules/@webassemblyjs/ieee754": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
- "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
+ "node_modules/@types/eslint": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
"license": "MIT",
"dependencies": {
- "@xtuc/ieee754": "^1.2.0"
+ "@types/estree": "*",
+ "@types/json-schema": "*"
}
},
- "node_modules/@webassemblyjs/leb128": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
- "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
- "license": "Apache-2.0",
+ "node_modules/@types/eslint-scope": {
+ "version": "3.7.7",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+ "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
+ "license": "MIT",
"dependencies": {
- "@xtuc/long": "4.2.2"
+ "@types/eslint": "*",
+ "@types/estree": "*"
}
},
- "node_modules/@webassemblyjs/utf8": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
- "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
+ "node_modules/@types/estree": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"license": "MIT"
},
- "node_modules/@webassemblyjs/wasm-edit": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
- "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
- "license": "MIT",
+ "node_modules/@types/graceful-fs": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
+ "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==",
+ "dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.14.1",
- "@webassemblyjs/helper-buffer": "1.14.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
- "@webassemblyjs/helper-wasm-section": "1.14.1",
- "@webassemblyjs/wasm-gen": "1.14.1",
- "@webassemblyjs/wasm-opt": "1.14.1",
- "@webassemblyjs/wasm-parser": "1.14.1",
- "@webassemblyjs/wast-printer": "1.14.1"
+ "@types/node": "*"
}
},
- "node_modules/@webassemblyjs/wasm-gen": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
- "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
- "license": "MIT",
+ "node_modules/@types/istanbul-lib-coverage": {
+ "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/istanbul-lib-report": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
+ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
+ "dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.14.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
- "@webassemblyjs/ieee754": "1.13.2",
- "@webassemblyjs/leb128": "1.13.2",
- "@webassemblyjs/utf8": "1.13.2"
+ "@types/istanbul-lib-coverage": "*"
}
},
- "node_modules/@webassemblyjs/wasm-opt": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
- "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
- "license": "MIT",
+ "node_modules/@types/istanbul-reports": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
+ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
+ "dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.14.1",
- "@webassemblyjs/helper-buffer": "1.14.1",
- "@webassemblyjs/wasm-gen": "1.14.1",
- "@webassemblyjs/wasm-parser": "1.14.1"
+ "@types/istanbul-lib-report": "*"
}
},
- "node_modules/@webassemblyjs/wasm-parser": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
- "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
- "license": "MIT",
+ "node_modules/@types/jest": {
+ "version": "29.5.14",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz",
+ "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==",
+ "dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.14.1",
- "@webassemblyjs/helper-api-error": "1.13.2",
- "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
- "@webassemblyjs/ieee754": "1.13.2",
- "@webassemblyjs/leb128": "1.13.2",
- "@webassemblyjs/utf8": "1.13.2"
+ "expect": "^29.0.0",
+ "pretty-format": "^29.0.0"
}
},
- "node_modules/@webassemblyjs/wast-printer": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
- "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/mocha": {
+ "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": {
+ "version": "20.17.14",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz",
+ "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==",
"license": "MIT",
"dependencies": {
- "@webassemblyjs/ast": "1.14.1",
- "@xtuc/long": "4.2.2"
+ "undici-types": "~6.19.2"
}
},
- "node_modules/@webpack-cli/configtest": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
- "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==",
+ "node_modules/@types/stack-utils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
+ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
+ "dev": true
+ },
+ "node_modules/@types/vscode": {
+ "version": "1.96.0",
+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz",
+ "integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==",
"dev": true,
- "license": "MIT",
+ "license": "MIT"
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.14",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz",
+ "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/yargs": {
+ "version": "17.0.33",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
+ "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==",
+ "dev": true,
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.3",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
+ "dev": true
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz",
+ "integrity": "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.24.1",
+ "@typescript-eslint/type-utils": "8.24.1",
+ "@typescript-eslint/utils": "8.24.1",
+ "@typescript-eslint/visitor-keys": "8.24.1",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.0.1"
+ },
"engines": {
- "node": ">=14.15.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "webpack": "5.x.x",
- "webpack-cli": "5.x.x"
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.8.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.1.tgz",
+ "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.24.1",
+ "@typescript-eslint/types": "8.24.1",
+ "@typescript-eslint/typescript-estree": "8.24.1",
+ "@typescript-eslint/visitor-keys": "8.24.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.8.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz",
+ "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.24.1",
+ "@typescript-eslint/visitor-keys": "8.24.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.1.tgz",
+ "integrity": "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.24.1",
+ "@typescript-eslint/utils": "8.24.1",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.0.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.8.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz",
+ "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz",
+ "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.24.1",
+ "@typescript-eslint/visitor-keys": "8.24.1",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.0.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.8.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz",
+ "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.24.1",
+ "@typescript-eslint/types": "8.24.1",
+ "@typescript-eslint/typescript-estree": "8.24.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.8.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.24.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz",
+ "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.24.1",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@vscode/test-cli": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.10.tgz",
+ "integrity": "sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mocha": "^10.0.2",
+ "c8": "^9.1.0",
+ "chokidar": "^3.5.3",
+ "enhanced-resolve": "^5.15.0",
+ "glob": "^10.3.10",
+ "minimatch": "^9.0.3",
+ "mocha": "^10.2.0",
+ "supports-color": "^9.4.0",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "vscode-test": "out/bin.mjs"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/@vscode/test-cli/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha": {
+ "version": "10.8.2",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz",
+ "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-colors": "^4.1.3",
+ "browser-stdout": "^1.3.1",
+ "chokidar": "^3.5.3",
+ "debug": "^4.3.5",
+ "diff": "^5.2.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-up": "^5.0.0",
+ "glob": "^8.1.0",
+ "he": "^1.2.0",
+ "js-yaml": "^4.1.0",
+ "log-symbols": "^4.1.0",
+ "minimatch": "^5.1.6",
+ "ms": "^2.1.3",
+ "serialize-javascript": "^6.0.2",
+ "strip-json-comments": "^3.1.1",
+ "supports-color": "^8.1.1",
+ "workerpool": "^6.5.1",
+ "yargs": "^16.2.0",
+ "yargs-parser": "^20.2.9",
+ "yargs-unparser": "^2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha.js"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/supports-color": {
+ "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"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@vscode/test-cli/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",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@vscode/test-cli/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@vscode/test-electron": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz",
+ "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==",
+ "dev": true,
+ "dependencies": {
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.5",
+ "jszip": "^3.10.1",
+ "ora": "^7.0.1",
+ "semver": "^7.6.2"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
+ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
+ "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
+ "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
+ "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-numbers": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
+ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
+ "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
+ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/wasm-gen": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/ieee754": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
+ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
+ "license": "MIT",
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "node_modules/@webassemblyjs/leb128": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
+ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/utf8": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
+ "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
+ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/helper-wasm-section": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-opt": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1",
+ "@webassemblyjs/wast-printer": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
+ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
+ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
+ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
+ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webpack-cli/configtest": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
+ "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.15.0"
+ },
+ "peerDependencies": {
+ "webpack": "5.x.x",
+ "webpack-cli": "5.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/info": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz",
+ "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.15.0"
+ },
+ "peerDependencies": {
+ "webpack": "5.x.x",
+ "webpack-cli": "5.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/serve": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz",
+ "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.15.0"
+ },
+ "peerDependencies": {
+ "webpack": "5.x.x",
+ "webpack-cli": "5.x.x"
+ },
+ "peerDependenciesMeta": {
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/acorn": {
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
+ "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-formats/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true
+ },
+ "node_modules/babel-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
+ "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/transform": "^29.7.0",
+ "@types/babel__core": "^7.1.14",
+ "babel-plugin-istanbul": "^6.1.1",
+ "babel-preset-jest": "^29.6.3",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.8.0"
+ }
+ },
+ "node_modules/babel-plugin-istanbul": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+ "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^5.0.4",
+ "test-exclude": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-istanbul/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/babel-plugin-jest-hoist": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
+ "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.3.3",
+ "@babel/types": "^7.3.3",
+ "@types/babel__core": "^7.1.14",
+ "@types/babel__traverse": "^7.0.6"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-preset-current-node-syntax": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz",
+ "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-bigint": "^7.8.3",
+ "@babel/plugin-syntax-class-properties": "^7.12.13",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5",
+ "@babel/plugin-syntax-import-attributes": "^7.24.7",
+ "@babel/plugin-syntax-import-meta": "^7.10.4",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+ "@babel/plugin-syntax-top-level-await": "^7.14.5"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/babel-preset-jest": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
+ "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
+ "dev": true,
+ "dependencies": {
+ "babel-plugin-jest-hoist": "^29.6.3",
+ "babel-preset-current-node-syntax": "^1.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/bl": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
+ "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^6.0.3",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/braces": {
+ "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"
+ },
+ "engines": {
+ "node": ">=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==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/browserslist": {
+ "version": "4.24.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
+ "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001688",
+ "electron-to-chromium": "^1.5.73",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.1"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bs-logger": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
+ "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
+ "dev": true,
+ "dependencies": {
+ "fast-json-stable-stringify": "2.x"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/bser": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+ "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+ "dev": true,
+ "dependencies": {
+ "node-int64": "^0.4.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "license": "MIT"
+ },
+ "node_modules/bufferutil": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
+ "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "node-gyp-build": "^4.3.0"
+ },
+ "engines": {
+ "node": ">=6.14.2"
+ }
+ },
+ "node_modules/c8": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
+ "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@bcoe/v8-coverage": "^0.2.3",
+ "@istanbuljs/schema": "^0.1.3",
+ "find-up": "^5.0.0",
+ "foreground-child": "^3.1.1",
+ "istanbul-lib-coverage": "^3.2.0",
+ "istanbul-lib-report": "^3.0.1",
+ "istanbul-reports": "^3.1.6",
+ "test-exclude": "^6.0.0",
+ "v8-to-istanbul": "^9.0.0",
+ "yargs": "^17.7.2",
+ "yargs-parser": "^21.1.1"
+ },
+ "bin": {
+ "c8": "bin/c8.js"
+ },
+ "engines": {
+ "node": ">=14.14.0"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001692",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
+ "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "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",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "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"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/chokidar": {
+ "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",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
+ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cjs-module-lexer": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
+ "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
+ "dev": true
+ },
+ "node_modules/cli-cursor": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
+ "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/emoji-regex": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+ "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+ "dev": true
+ },
+ "node_modules/cli-truncate/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cliui": {
+ "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",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/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",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+ "dev": true,
+ "engines": {
+ "iojs": ">= 1.0.0",
+ "node": ">= 0.12.0"
+ }
+ },
+ "node_modules/collect-v8-coverage": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
+ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
+ "dev": true
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "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": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/create-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
+ "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.9",
+ "jest-config": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "prompts": "^2.0.1"
+ },
+ "bin": {
+ "create-jest": "bin/create-jest.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/cross-spawn": {
+ "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",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-loader": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz",
+ "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.1.0",
+ "postcss": "^8.4.33",
+ "postcss-modules-extract-imports": "^3.1.0",
+ "postcss-modules-local-by-default": "^4.0.5",
+ "postcss-modules-scope": "^3.2.0",
+ "postcss-modules-values": "^4.0.0",
+ "postcss-value-parser": "^4.2.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "@rspack/core": "0.x || 1.x",
+ "webpack": "^5.27.0"
+ },
+ "peerDependenciesMeta": {
+ "@rspack/core": {
+ "optional": true
+ },
+ "webpack": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/debug": {
+ "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"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/dedent": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
+ "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==",
+ "dev": true,
+ "peerDependencies": {
+ "babel-plugin-macros": "^3.1.0"
+ },
+ "peerDependenciesMeta": {
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/detect-newline": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/diff": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+ "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/diff-sequences": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
+ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dotenv-defaults": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz",
+ "integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==",
+ "dependencies": {
+ "dotenv": "^8.2.0"
+ }
+ },
+ "node_modules/dotenv-defaults/node_modules/dotenv": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
+ "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/dotenv-webpack": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.1.0.tgz",
+ "integrity": "sha512-owK1JcsPkIobeqjVrk6h7jPED/W6ZpdFsMPR+5ursB7/SdgDyO+VzAU+szK8C8u3qUhtENyYnj8eyXMR5kkGag==",
+ "dependencies": {
+ "dotenv-defaults": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "webpack": "^4 || ^5"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ejs": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
+ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
+ "dev": true,
+ "dependencies": {
+ "jake": "^10.8.5"
+ },
+ "bin": {
+ "ejs": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.83",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz",
+ "integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==",
+ "license": "ISC"
+ },
+ "node_modules/emittery": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
+ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
+ "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/envinfo": {
+ "version": "7.14.0",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz",
+ "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "envinfo": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
+ "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
+ "license": "MIT"
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
}
},
- "node_modules/@webpack-cli/info": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz",
- "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==",
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=14.15.0"
+ "node": ">=10"
},
- "peerDependencies": {
- "webpack": "5.x.x",
- "webpack-cli": "5.x.x"
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@webpack-cli/serve": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz",
- "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==",
+ "node_modules/eslint": {
+ "version": "9.21.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
+ "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.19.2",
+ "@eslint/core": "^0.12.0",
+ "@eslint/eslintrc": "^3.3.0",
+ "@eslint/js": "9.21.0",
+ "@eslint/plugin-kit": "^0.2.7",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.2.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
"engines": {
- "node": ">=14.15.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
},
"peerDependencies": {
- "webpack": "5.x.x",
- "webpack-cli": "5.x.x"
+ "jiti": "*"
},
"peerDependenciesMeta": {
- "webpack-dev-server": {
+ "jiti": {
"optional": true
}
}
},
- "node_modules/@xtuc/ieee754": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
- "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@xtuc/long": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
- "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
- "license": "Apache-2.0"
- },
- "node_modules/acorn": {
- "version": "8.14.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
- "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
- "license": "MIT",
+ "node_modules/eslint-config-prettier": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz",
+ "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==",
+ "dev": true,
"bin": {
- "acorn": "bin/acorn"
+ "eslint-config-prettier": "build/bin/cli.js"
},
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-jsx": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true,
"peerDependencies": {
- "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ "eslint": ">=7.0.0"
}
},
- "node_modules/agent-base": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
- "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz",
+ "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==",
"dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "license": "MIT",
"dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.9.1"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
},
"funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/ajv-formats": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
- "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
- "license": "MIT",
- "dependencies": {
- "ajv": "^8.0.0"
+ "url": "https://opencollective.com/eslint-plugin-prettier"
},
"peerDependencies": {
- "ajv": "^8.0.0"
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": "*",
+ "prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
- "ajv": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
"optional": true
}
}
},
- "node_modules/ajv-formats/node_modules/ajv": {
- "version": "8.17.1",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
- "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.3",
- "fast-uri": "^3.0.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/ajv-formats/node_modules/json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "license": "MIT"
- },
- "node_modules/ajv-keywords": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
- "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
- "license": "MIT",
+ "node_modules/eslint-plugin-unused-imports": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz",
+ "integrity": "sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==",
+ "dev": true,
"peerDependencies": {
- "ajv": "^6.9.1"
+ "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0",
+ "eslint": "^9.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/eslint-plugin": {
+ "optional": true
+ }
}
},
- "node_modules/ansi-colors": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
- "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "node_modules/eslint-scope": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+ "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
"dev": true,
- "license": "MIT",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
"engines": {
- "node": ">=6"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/ansi-escapes": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
- "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
- "dependencies": {
- "environment": "^1.0.0"
- },
+ "license": "Apache-2.0",
"engines": {
- "node": ">=18"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "license": "Apache-2.0",
"engines": {
- "node": ">=12"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
- "license": "MIT",
+ "license": "ISC",
"dependencies": {
- "color-convert": "^2.0.1"
+ "is-glob": "^4.0.3"
},
"engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ "node": ">=10.13.0"
}
},
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
+ "brace-expansion": "^1.1.7"
},
"engines": {
- "node": ">= 8"
+ "node": "*"
}
},
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true,
- "license": "Python-2.0"
- },
- "node_modules/assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "node_modules/espree": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
"engines": {
- "node": ">=12"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true,
- "license": "MIT",
"engines": {
- "node": ">=8"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/bl": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
- "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==",
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "buffer": "^6.0.3",
- "inherits": "^2.0.4",
- "readable-stream": "^3.4.0"
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "node_modules/bl/node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
"dev": true,
- "license": "MIT",
+ "license": "BSD-3-Clause",
"dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
+ "estraverse": "^5.1.0"
},
"engines": {
- "node": ">= 6"
+ "node": ">=0.10"
}
},
- "node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "license": "MIT",
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "license": "BSD-2-Clause",
"dependencies": {
- "balanced-match": "^1.0.0"
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
}
},
- "node_modules/braces": {
- "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"
- },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "license": "BSD-2-Clause",
"engines": {
- "node": ">=8"
+ "node": ">=4.0"
}
},
- "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==",
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true,
- "license": "ISC"
- },
- "node_modules/browserslist": {
- "version": "4.24.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
- "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "caniuse-lite": "^1.0.30001688",
- "electron-to-chromium": "^1.5.73",
- "node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.1"
- },
- "bin": {
- "browserslist": "cli.js"
- },
+ "license": "BSD-2-Clause",
"engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ "node": ">=0.10.0"
}
},
- "node_modules/buffer": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
- "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
- "dependencies": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.2.1"
+ "engines": {
+ "node": ">=0.8.x"
}
},
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "license": "MIT"
- },
- "node_modules/bufferutil": {
- "version": "4.0.9",
- "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
- "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==",
- "hasInstallScript": true,
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
"dependencies": {
- "node-gyp-build": "^4.3.0"
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
},
"engines": {
- "node": ">=6.14.2"
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
- "node_modules/c8": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
- "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==",
+ "node_modules/execa/node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"dev": true,
- "license": "ISC",
- "dependencies": {
- "@bcoe/v8-coverage": "^0.2.3",
- "@istanbuljs/schema": "^0.1.3",
- "find-up": "^5.0.0",
- "foreground-child": "^3.1.1",
- "istanbul-lib-coverage": "^3.2.0",
- "istanbul-lib-report": "^3.0.1",
- "istanbul-reports": "^3.1.6",
- "test-exclude": "^6.0.0",
- "v8-to-istanbul": "^9.0.0",
- "yargs": "^17.7.2",
- "yargs-parser": "^21.1.1"
+ "engines": {
+ "node": ">=12"
},
- "bin": {
- "c8": "bin/c8.js"
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/execa/node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
},
"engines": {
- "node": ">=14.14.0"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
"dev": true,
"engines": {
- "node": ">=6"
+ "node": ">= 0.8.0"
}
},
- "node_modules/camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "node_modules/expect": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
+ "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "@jest/expect-utils": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0"
+ },
"engines": {
- "node": ">=10"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": ">=8.6.0"
}
},
- "node_modules/caniuse-lite": {
- "version": "1.0.30001692",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
- "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz",
+ "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==",
"funding": [
{
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
},
{
- "type": "github",
- "url": "https://github.com/sponsors/ai"
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
}
],
- "license": "CC-BY-4.0"
+ "license": "BSD-3-Clause"
},
- "node_modules/chai": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz",
- "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==",
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "assertion-error": "^2.0.1",
- "check-error": "^2.1.1",
- "deep-eql": "^5.0.1",
- "loupe": "^3.1.0",
- "pathval": "^2.0.0"
- },
"engines": {
- "node": ">=12"
+ "node": ">= 4.9.1"
}
},
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "node_modules/fastq": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
+ "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fb-watchman": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
+ "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+ "dev": true,
+ "dependencies": {
+ "bser": "2.1.1"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
+ "flat-cache": "^4.0.0"
},
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
+ "node": ">=16.0.0"
}
},
- "node_modules/chalk/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "node_modules/filelist": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
+ "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "has-flag": "^4.0.0"
+ "minimatch": "^5.0.1"
+ }
+ },
+ "node_modules/filelist/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
},
"engines": {
- "node": ">=8"
+ "node": ">=10"
}
},
- "node_modules/check-error": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
- "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+ "node_modules/fill-range": {
+ "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"
+ },
"engines": {
- "node": ">= 16"
+ "node": ">=8"
}
},
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"license": "MIT",
"dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
},
"engines": {
- "node": ">= 8.10.0"
+ "node": ">=10"
},
"funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/chrome-trace-event": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
- "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
"license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
"engines": {
- "node": ">=6.0"
+ "node": ">=16"
}
},
- "node_modules/cli-cursor": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
- "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+ "node_modules/flatted": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
+ "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
+ "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
"dev": true,
- "license": "MIT",
+ "license": "ISC",
"dependencies": {
- "restore-cursor": "^4.0.0"
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
},
"engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ "node": ">=14"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/cli-spinners": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
- "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
+ "hasInstallScript": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
- "node_modules/cli-truncate": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
- "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
- "dependencies": {
- "slice-ansi": "^5.0.0",
- "string-width": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- },
+ "license": "MIT",
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/cli-truncate/node_modules/emoji-regex": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
- "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
- "dev": true
- },
- "node_modules/cli-truncate/node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
- "dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
- },
"engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">=6.9.0"
}
},
- "node_modules/cliui": {
- "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==",
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
"engines": {
- "node": ">=12"
+ "node": "6.* || 8.* || >= 10.*"
}
},
- "node_modules/cliui/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "node_modules/get-east-asian-width": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+ "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
"dev": true,
- "license": "MIT",
"engines": {
- "node": ">=8"
- }
- },
- "node_modules/cliui/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==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cliui/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
+ "node": ">=18"
},
- "engines": {
- "node": ">=8"
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/cliui/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
"engines": {
- "node": ">=8"
+ "node": ">=8.0.0"
}
},
- "node_modules/cliui/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==",
+ "node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
"engines": {
- "node": ">=10"
+ "node": ">=16"
},
"funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/clone-deep": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
- "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "node_modules/glob": {
+ "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": "MIT",
+ "license": "ISC",
"dependencies": {
- "is-plain-object": "^2.0.4",
- "kind-of": "^6.0.2",
- "shallow-clone": "^3.0.0"
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
},
- "engines": {
- "node": ">=6"
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
- "license": "MIT",
+ "license": "ISC",
"dependencies": {
- "color-name": "~1.1.4"
+ "is-glob": "^4.0.1"
},
"engines": {
- "node": ">=7.0.0"
+ "node": ">= 6"
}
},
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/colorette": {
- "version": "2.0.20",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
- "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "license": "MIT"
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+ "license": "BSD-2-Clause"
},
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
- "license": "MIT"
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "node_modules/convert-source-map": {
- "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/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==",
+ "license": "ISC"
},
- "node_modules/core-util-is": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
- "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true,
"license": "MIT"
},
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"license": "MIT",
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
"engines": {
- "node": ">= 8"
+ "node": ">=8"
}
},
- "node_modules/css-loader": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz",
- "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==",
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "icss-utils": "^5.1.0",
- "postcss": "^8.4.33",
- "postcss-modules-extract-imports": "^3.1.0",
- "postcss-modules-local-by-default": "^4.0.5",
- "postcss-modules-scope": "^3.2.0",
- "postcss-modules-values": "^4.0.0",
- "postcss-value-parser": "^4.2.0",
- "semver": "^7.5.4"
+ "function-bind": "^1.1.2"
},
"engines": {
- "node": ">= 18.12.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- },
- "peerDependencies": {
- "@rspack/core": "0.x || 1.x",
- "webpack": "^5.27.0"
- },
- "peerDependenciesMeta": {
- "@rspack/core": {
- "optional": true
- },
- "webpack": {
- "optional": true
- }
+ "node": ">= 0.4"
}
},
- "node_modules/cssesc": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true,
+ "license": "MIT",
"bin": {
- "cssesc": "bin/cssesc"
- },
- "engines": {
- "node": ">=4"
+ "he": "bin/he"
}
},
- "node_modules/debug": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "node_modules/html-escaper": {
+ "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": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
"license": "MIT",
"dependencies": {
- "ms": "^2.1.3"
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
},
"engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
+ "node": ">= 14"
}
},
- "node_modules/decamelize": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
- "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"license": "MIT",
- "engines": {
- "node": ">=10"
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": ">= 14"
}
},
- "node_modules/deep-eql": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+ "node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
"dev": true,
- "license": "MIT",
"engines": {
- "node": ">=6"
+ "node": ">=16.17.0"
}
},
- "node_modules/deep-is": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/diff": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
- "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
+ "node_modules/husky": {
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
+ "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
"dev": true,
- "license": "BSD-3-Clause",
+ "bin": {
+ "husky": "bin.js"
+ },
"engines": {
- "node": ">=0.3.1"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
}
},
- "node_modules/dotenv": {
- "version": "16.4.7",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
- "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
- "license": "BSD-2-Clause",
+ "node_modules/icss-utils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "dev": true,
"engines": {
- "node": ">=12"
+ "node": "^10 || ^12 || >= 14"
},
- "funding": {
- "url": "https://dotenvx.com"
+ "peerDependencies": {
+ "postcss": "^8.1.0"
}
},
- "node_modules/dotenv-defaults": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz",
- "integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==",
- "dependencies": {
- "dotenv": "^8.2.0"
- }
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
},
- "node_modules/dotenv-defaults/node_modules/dotenv": {
- "version": "8.6.0",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
- "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
"engines": {
- "node": ">=10"
+ "node": ">= 4"
}
},
- "node_modules/dotenv-webpack": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.1.0.tgz",
- "integrity": "sha512-owK1JcsPkIobeqjVrk6h7jPED/W6ZpdFsMPR+5ursB7/SdgDyO+VzAU+szK8C8u3qUhtENyYnj8eyXMR5kkGag==",
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
"dependencies": {
- "dotenv-defaults": "^2.0.2"
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
},
"engines": {
- "node": ">=10"
+ "node": ">=6"
},
- "peerDependencies": {
- "webpack": "^4 || ^5"
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/electron-to-chromium": {
- "version": "1.5.83",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz",
- "integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==",
- "license": "ISC"
- },
- "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==",
+ "node_modules/import-local": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
+ "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
"dev": true,
- "license": "MIT"
- },
- "node_modules/enhanced-resolve": {
- "version": "5.18.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
- "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
"license": "MIT",
"dependencies": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
},
"engines": {
- "node": ">=10.13.0"
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/envinfo": {
- "version": "7.14.0",
- "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz",
- "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==",
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"license": "MIT",
- "bin": {
- "envinfo": "dist/cli.js"
- },
"engines": {
- "node": ">=4"
+ "node": ">=0.8.19"
}
},
- "node_modules/environment": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
- "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
}
},
- "node_modules/es-module-lexer": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
- "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
- "license": "MIT"
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
},
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "node_modules/interpret": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
+ "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==",
+ "dev": true,
"license": "MIT",
"engines": {
- "node": ">=6"
+ "node": ">=10.13.0"
}
},
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "node_modules/is-binary-path": {
+ "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,
"license": "MIT",
- "engines": {
- "node": ">=10"
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": ">=8"
}
},
- "node_modules/eslint": {
- "version": "9.21.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
- "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.19.2",
- "@eslint/core": "^0.12.0",
- "@eslint/eslintrc": "^3.3.0",
- "@eslint/js": "9.21.0",
- "@eslint/plugin-kit": "^0.2.7",
- "@humanfs/node": "^0.16.6",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.2",
- "@types/estree": "^1.0.6",
- "@types/json-schema": "^7.0.15",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.6",
- "debug": "^4.3.2",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.2.0",
- "eslint-visitor-keys": "^4.2.0",
- "espree": "^10.3.0",
- "esquery": "^1.5.0",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^8.0.0",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "ignore": "^5.2.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.3"
- },
- "bin": {
- "eslint": "bin/eslint.js"
+ "hasown": "^2.0.2"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://eslint.org/donate"
- },
- "peerDependencies": {
- "jiti": "*"
- },
- "peerDependenciesMeta": {
- "jiti": {
- "optional": true
- }
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/eslint-config-prettier": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz",
- "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==",
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
- "bin": {
- "eslint-config-prettier": "build/bin/cli.js"
- },
- "peerDependencies": {
- "eslint": ">=7.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "node_modules/eslint-plugin-prettier": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz",
- "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==",
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
- "dependencies": {
- "prettier-linter-helpers": "^1.0.0",
- "synckit": "^0.9.1"
- },
+ "license": "MIT",
"engines": {
- "node": "^14.18.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint-plugin-prettier"
- },
- "peerDependencies": {
- "@types/eslint": ">=8.0.0",
- "eslint": ">=8.0.0",
- "eslint-config-prettier": "*",
- "prettier": ">=3.0.0"
- },
- "peerDependenciesMeta": {
- "@types/eslint": {
- "optional": true
- },
- "eslint-config-prettier": {
- "optional": true
- }
+ "node": ">=8"
}
},
- "node_modules/eslint-plugin-unused-imports": {
- "version": "4.1.4",
- "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz",
- "integrity": "sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==",
+ "node_modules/is-generator-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
"dev": true,
- "peerDependencies": {
- "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0",
- "eslint": "^9.0.0 || ^8.0.0"
- },
- "peerDependenciesMeta": {
- "@typescript-eslint/eslint-plugin": {
- "optional": true
- }
+ "engines": {
+ "node": ">=6"
}
},
- "node_modules/eslint-scope": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
- "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
- "license": "BSD-2-Clause",
+ "license": "MIT",
"dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
+ "is-extglob": "^2.1.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "node": ">=0.10.0"
}
},
- "node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "node_modules/is-interactive": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
+ "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
"dev": true,
- "license": "Apache-2.0",
+ "license": "MIT",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": ">=12"
},
"funding": {
- "url": "https://opencollective.com/eslint"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "node_modules/is-number": {
+ "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",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint/node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
- "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
- "dev": true,
- "license": "Apache-2.0",
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "node": ">=0.12.0"
}
},
- "node_modules/eslint/node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
"dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.3"
- },
+ "license": "MIT",
"engines": {
- "node": ">=10.13.0"
+ "node": ">=8"
}
},
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "isobject": "^3.0.1"
},
"engines": {
- "node": "*"
+ "node": ">=0.10.0"
}
},
- "node_modules/espree": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
- "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"dev": true,
- "dependencies": {
- "acorn": "^8.14.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.0"
- },
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
- "url": "https://opencollective.com/eslint"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/espree/node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
- "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
"dev": true,
+ "license": "MIT",
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": ">=10"
},
"funding": {
- "url": "https://opencollective.com/eslint"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "estraverse": "^5.1.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/esrecurse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": ">=4.0"
- }
+ "license": "MIT"
},
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=4.0"
- }
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
},
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
"dev": true,
- "license": "BSD-2-Clause",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
- "node_modules/eventemitter3": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
- "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
- "dev": true
- },
- "node_modules/events": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
- "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
- "license": "MIT",
+ "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": ">=0.8.x"
+ "node": ">=8"
}
},
- "node_modules/execa": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
- "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "node_modules/istanbul-lib-instrument": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
"dev": true,
"dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^8.0.1",
- "human-signals": "^5.0.0",
- "is-stream": "^3.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^5.1.0",
- "onetime": "^6.0.0",
- "signal-exit": "^4.1.0",
- "strip-final-newline": "^3.0.0"
+ "@babel/core": "^7.23.9",
+ "@babel/parser": "^7.23.9",
+ "@istanbuljs/schema": "^0.1.3",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^7.5.4"
},
"engines": {
- "node": ">=16.17"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ "node": ">=10"
}
},
- "node_modules/execa/node_modules/mimic-fn": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
- "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "node_modules/istanbul-lib-report": {
+ "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,
- "engines": {
- "node": ">=12"
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^4.0.0",
+ "supports-color": "^7.1.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": ">=10"
}
},
- "node_modules/execa/node_modules/onetime": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
- "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "node_modules/istanbul-lib-report/node_modules/supports-color": {
+ "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": {
- "mimic-fn": "^4.0.0"
+ "has-flag": "^4.0.0"
},
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">=8"
}
},
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "license": "MIT"
- },
- "node_modules/fast-diff": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
- "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
- "dev": true
- },
- "node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+ "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
"dev": true,
"dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
},
"engines": {
- "node": ">=8.6.0"
+ "node": ">=10"
}
},
- "node_modules/fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "license": "MIT"
- },
- "node_modules/fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "node_modules/istanbul-lib-source-maps/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
- "license": "MIT"
- },
- "node_modules/fast-uri": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz",
- "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fastify"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/fastify"
- }
- ],
- "license": "BSD-3-Clause"
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "node_modules/fastest-levenshtein": {
- "version": "1.0.16",
- "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
- "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "node_modules/istanbul-reports": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
+ "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
"dev": true,
- "license": "MIT",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
"engines": {
- "node": ">= 4.9.1"
+ "node": ">=8"
}
},
- "node_modules/fastq": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
- "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
+ "node_modules/jackspeak": {
+ "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": {
- "reusify": "^1.0.4"
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
}
},
- "node_modules/file-entry-cache": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
- "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "node_modules/jake": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
+ "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "flat-cache": "^4.0.0"
+ "async": "^3.2.3",
+ "chalk": "^4.0.2",
+ "filelist": "^1.0.4",
+ "minimatch": "^3.1.2"
+ },
+ "bin": {
+ "jake": "bin/cli.js"
},
"engines": {
- "node": ">=16.0.0"
+ "node": ">=10"
}
},
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "node_modules/jake/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
- "node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "node_modules/jake/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
+ "brace-expansion": "^1.1.7"
},
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": "*"
}
},
- "node_modules/flat": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
- "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "node_modules/jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
+ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
"dev": true,
- "license": "BSD-3-Clause",
+ "dependencies": {
+ "@jest/core": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "import-local": "^3.0.2",
+ "jest-cli": "^29.7.0"
+ },
"bin": {
- "flat": "cli.js"
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
}
},
- "node_modules/flat-cache": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
- "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "node_modules/jest-changed-files": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
+ "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.4"
+ "execa": "^5.0.0",
+ "jest-util": "^29.7.0",
+ "p-limit": "^3.1.0"
},
"engines": {
- "node": ">=16"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/flatted": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
- "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/foreground-child": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
- "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "node_modules/jest-changed-files/node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
"dev": true,
- "license": "ISC",
"dependencies": {
- "cross-spawn": "^7.0.0",
- "signal-exit": "^4.0.1"
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
},
"engines": {
- "node": ">=14"
+ "node": ">=10"
},
"funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "node_modules/jest-changed-files/node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
"dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
"engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
- "license": "MIT",
+ "node": ">=10"
+ },
"funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "node_modules/jest-changed-files/node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
"dev": true,
- "license": "ISC",
"engines": {
- "node": "6.* || 8.* || >= 10.*"
+ "node": ">=10.17.0"
}
},
- "node_modules/get-east-asian-width": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
- "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+ "node_modules/jest-changed-files/node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"dev": true,
"engines": {
- "node": ">=18"
+ "node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/get-stream": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
- "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "node_modules/jest-changed-files/node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
"dev": true,
- "engines": {
- "node": ">=16"
+ "dependencies": {
+ "path-key": "^3.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": ">=8"
}
},
- "node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "node_modules/jest-changed-files/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "node_modules/jest-changed-files/node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
"dev": true,
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^3.1.2",
- "minimatch": "^9.0.4",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^1.11.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "engines": {
+ "node": ">=6"
}
},
- "node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "node_modules/jest-circus": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
+ "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
"dev": true,
- "license": "ISC",
"dependencies": {
- "is-glob": "^4.0.1"
+ "@jest/environment": "^29.7.0",
+ "@jest/expect": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "co": "^4.6.0",
+ "dedent": "^1.0.0",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^29.7.0",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "p-limit": "^3.1.0",
+ "pretty-format": "^29.7.0",
+ "pure-rand": "^6.0.0",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-cli": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
+ "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/core": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "create-jest": "^29.7.0",
+ "exit": "^0.1.2",
+ "import-local": "^3.0.2",
+ "jest-config": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "yargs": "^17.3.1"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
},
"engines": {
- "node": ">= 6"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
}
},
- "node_modules/glob-to-regexp": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
- "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
- "license": "BSD-2-Clause"
- },
- "node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "node_modules/jest-config": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
+ "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
"dev": true,
+ "dependencies": {
+ "@babel/core": "^7.11.6",
+ "@jest/test-sequencer": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "babel-jest": "^29.7.0",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "deepmerge": "^4.2.2",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "jest-circus": "^29.7.0",
+ "jest-environment-node": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-runner": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "parse-json": "^5.2.0",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "strip-json-comments": "^3.1.1"
+ },
"engines": {
- "node": ">=18"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "peerDependencies": {
+ "@types/node": "*",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
}
},
- "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==",
- "license": "ISC"
- },
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "node_modules/jest-config/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
- "license": "MIT"
- },
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
- "node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "node_modules/jest-config/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
- "license": "MIT",
"dependencies": {
- "function-bind": "^1.1.2"
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
},
"engines": {
- "node": ">= 0.4"
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "node_modules/jest-config/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
- "license": "MIT",
- "bin": {
- "he": "bin/he"
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
}
},
- "node_modules/html-escaper": {
- "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": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "node_modules/jest-diff": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
+ "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
+ "chalk": "^4.0.0",
+ "diff-sequences": "^29.6.3",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
},
"engines": {
- "node": ">= 14"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/https-proxy-agent": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
- "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "node_modules/jest-docblock": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
+ "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "agent-base": "^7.1.2",
- "debug": "4"
+ "detect-newline": "^3.0.0"
},
"engines": {
- "node": ">= 14"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/human-signals": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
- "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "node_modules/jest-each": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
+ "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
"dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "pretty-format": "^29.7.0"
+ },
"engines": {
- "node": ">=16.17.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/husky": {
- "version": "9.1.7",
- "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
- "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
+ "node_modules/jest-environment-node": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
+ "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
"dev": true,
- "bin": {
- "husky": "bin.js"
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "jest-mock": "^29.7.0",
+ "jest-util": "^29.7.0"
},
"engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/typicode"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/icss-utils": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
- "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "node_modules/jest-get-type": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
+ "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
"dev": true,
"engines": {
- "node": "^10 || ^12 || >= 14"
- },
- "peerDependencies": {
- "postcss": "^8.1.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/ieee754": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "BSD-3-Clause"
- },
- "node_modules/ignore": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
- "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "node_modules/jest-haste-map": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
+ "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/graceful-fs": "^4.1.3",
+ "@types/node": "*",
+ "anymatch": "^3.0.3",
+ "fb-watchman": "^2.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "walker": "^1.0.8"
+ },
"engines": {
- "node": ">= 4"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "^2.3.2"
}
},
- "node_modules/immediate": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
- "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/import-fresh": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "node_modules/jest-haste-map/node_modules/jest-worker": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
"dev": true,
"dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
+ "@types/node": "*",
+ "jest-util": "^29.7.0",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
},
"engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/import-local": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
- "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
+ "node_modules/jest-haste-map/node_modules/supports-color": {
+ "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": {
- "pkg-dir": "^4.2.0",
- "resolve-cwd": "^3.0.0"
- },
- "bin": {
- "import-local-fixture": "fixtures/cli.js"
+ "has-flag": "^4.0.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=10"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "node_modules/jest-leak-detector": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
+ "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
"engines": {
- "node": ">=0.8.19"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "node_modules/jest-matcher-utils": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
+ "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
"dev": true,
- "license": "ISC",
"dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
+ "chalk": "^4.0.0",
+ "jest-diff": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/interpret": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
- "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==",
+ "node_modules/jest-message-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
+ "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.12.13",
+ "@jest/types": "^29.6.3",
+ "@types/stack-utils": "^2.0.0",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ },
"engines": {
- "node": ">=10.13.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "node_modules/jest-mock": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
+ "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "binary-extensions": "^2.0.0"
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "jest-util": "^29.7.0"
},
"engines": {
- "node": ">=8"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/is-core-module": {
- "version": "2.16.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
- "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "node_modules/jest-pnp-resolver": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
+ "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.2"
- },
"engines": {
- "node": ">= 0.4"
+ "node": ">=6"
},
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "peerDependencies": {
+ "jest-resolve": "*"
+ },
+ "peerDependenciesMeta": {
+ "jest-resolve": {
+ "optional": true
+ }
}
},
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "node_modules/jest-regex-util": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
+ "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
"dev": true,
- "license": "MIT",
"engines": {
- "node": ">=0.10.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "node_modules/jest-resolve": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
+ "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-pnp-resolver": "^1.2.2",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "resolve": "^1.20.0",
+ "resolve.exports": "^2.0.0",
+ "slash": "^3.0.0"
+ },
"engines": {
- "node": ">=8"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "node_modules/jest-resolve-dependencies": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
+ "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "is-extglob": "^2.1.1"
+ "jest-regex-util": "^29.6.3",
+ "jest-snapshot": "^29.7.0"
},
"engines": {
- "node": ">=0.10.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/is-interactive": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
- "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
+ "node_modules/jest-runner": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
+ "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
"dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
+ "dependencies": {
+ "@jest/console": "^29.7.0",
+ "@jest/environment": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "emittery": "^0.13.1",
+ "graceful-fs": "^4.2.9",
+ "jest-docblock": "^29.7.0",
+ "jest-environment-node": "^29.7.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-leak-detector": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-resolve": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-watcher": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "p-limit": "^3.1.0",
+ "source-map-support": "0.5.13"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "node_modules/jest-runner/node_modules/jest-worker": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "jest-util": "^29.7.0",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
"engines": {
- "node": ">=0.12.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "node_modules/jest-runner/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
- "license": "MIT",
"engines": {
- "node": ">=8"
+ "node": ">=0.10.0"
}
},
- "node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "node_modules/jest-runner/node_modules/source-map-support": {
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
}
},
- "node_modules/is-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
- "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "node_modules/jest-runner/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
"engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ "node": ">=10"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "node_modules/is-unicode-supported": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
- "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "node_modules/jest-runtime": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
+ "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
"dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/globals": "^29.7.0",
+ "@jest/source-map": "^29.6.3",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "cjs-module-lexer": "^1.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-mock": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "slash": "^3.0.0",
+ "strip-bom": "^4.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "node_modules/jest-runtime/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
- "license": "MIT"
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
},
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "node_modules/jest-runtime/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
- "license": "ISC"
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
},
- "node_modules/isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "node_modules/jest-runtime/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
- "license": "MIT",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
"engines": {
- "node": ">=0.10.0"
+ "node": "*"
}
},
- "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==",
+ "node_modules/jest-runtime/node_modules/strip-bom": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
"dev": true,
- "license": "BSD-3-Clause",
"engines": {
"node": ">=8"
}
},
- "node_modules/istanbul-lib-report": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
- "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+ "node_modules/jest-snapshot": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
+ "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
"dev": true,
- "license": "BSD-3-Clause",
"dependencies": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^4.0.0",
- "supports-color": "^7.1.0"
+ "@babel/core": "^7.11.6",
+ "@babel/generator": "^7.7.2",
+ "@babel/plugin-syntax-jsx": "^7.7.2",
+ "@babel/plugin-syntax-typescript": "^7.7.2",
+ "@babel/types": "^7.3.3",
+ "@jest/expect-utils": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "babel-preset-current-node-syntax": "^1.0.0",
+ "chalk": "^4.0.0",
+ "expect": "^29.7.0",
+ "graceful-fs": "^4.2.9",
+ "jest-diff": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "natural-compare": "^1.4.0",
+ "pretty-format": "^29.7.0",
+ "semver": "^7.5.3"
},
"engines": {
- "node": ">=10"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/istanbul-lib-report/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "node_modules/jest-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
+ "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "has-flag": "^4.0.0"
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
},
"engines": {
- "node": ">=8"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/istanbul-reports": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
- "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
+ "node_modules/jest-validate": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
+ "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
"dev": true,
- "license": "BSD-3-Clause",
"dependencies": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
+ "@jest/types": "^29.6.3",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^29.6.3",
+ "leven": "^3.1.0",
+ "pretty-format": "^29.7.0"
},
"engines": {
- "node": ">=8"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/jackspeak": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
- "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "node_modules/jest-watcher": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
+ "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
"dev": true,
- "license": "BlueOak-1.0.0",
"dependencies": {
- "@isaacs/cliui": "^8.0.2"
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "emittery": "^0.13.1",
+ "jest-util": "^29.7.0",
+ "string-length": "^4.0.1"
},
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-watcher/node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.21.3"
},
- "optionalDependencies": {
- "@pkgjs/parseargs": "^0.11.0"
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/jest-worker": {
@@ -3287,6 +5631,12 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -3300,6 +5650,18 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -3326,6 +5688,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -3339,13 +5713,6 @@
"setimmediate": "^1.0.5"
}
},
- "node_modules/just-extend": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz",
- "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -3366,6 +5733,24 @@
"node": ">=0.10.0"
}
},
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -3402,6 +5787,12 @@
"url": "https://github.com/sponsors/antonk52"
}
},
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
"node_modules/lint-staged": {
"version": "15.4.3",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.4.3.tgz",
@@ -3544,13 +5935,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/lodash.get": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
- "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
- "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
- "dev": true,
- "license": "MIT"
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true
},
"node_modules/lodash.merge": {
"version": "4.6.2",
@@ -3724,13 +6113,6 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "node_modules/loupe": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz",
- "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
@@ -3754,6 +6136,21 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "node_modules/makeerror": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+ "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+ "dev": true,
+ "dependencies": {
+ "tmpl": "1.0.5"
+ }
+ },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -3845,76 +6242,11 @@
"node_modules/minipass": {
"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"
- }
- },
- "node_modules/mocha": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz",
- "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-colors": "^4.1.3",
- "browser-stdout": "^1.3.1",
- "chokidar": "^3.5.3",
- "debug": "^4.3.5",
- "diff": "^5.2.0",
- "escape-string-regexp": "^4.0.0",
- "find-up": "^5.0.0",
- "glob": "^10.4.5",
- "he": "^1.2.0",
- "js-yaml": "^4.1.0",
- "log-symbols": "^4.1.0",
- "minimatch": "^5.1.6",
- "ms": "^2.1.3",
- "serialize-javascript": "^6.0.2",
- "strip-json-comments": "^3.1.1",
- "supports-color": "^8.1.1",
- "workerpool": "^6.5.1",
- "yargs": "^17.7.2",
- "yargs-parser": "^21.1.1",
- "yargs-unparser": "^2.0.0"
- },
- "bin": {
- "_mocha": "bin/_mocha",
- "mocha": "bin/mocha.js"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/mocha/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/mocha/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
+ "license": "ISC",
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
+ "node": ">=16 || 14 >=14.17"
}
},
"node_modules/ms": {
@@ -3955,20 +6287,6 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"license": "MIT"
},
- "node_modules/nise": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz",
- "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@sinonjs/commons": "^3.0.1",
- "@sinonjs/fake-timers": "^13.0.1",
- "@sinonjs/text-encoding": "^0.7.3",
- "just-extend": "^6.2.0",
- "path-to-regexp": "^8.1.0"
- }
- },
"node_modules/node-gyp-build": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
@@ -3979,6 +6297,12 @@
"node-gyp-build-test": "build-test.js"
}
},
+ "node_modules/node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+ "dev": true
+ },
"node_modules/node-releases": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
@@ -4226,6 +6550,24 @@
"node": ">=6"
}
},
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4280,26 +6622,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/path-to-regexp": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
- "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/pathval": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
- "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14.16"
- }
- },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -4331,6 +6653,15 @@
"node": ">=0.10"
}
},
+ "node_modules/pirates": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -4621,6 +6952,32 @@
}
}
},
+ "node_modules/pretty-format": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -4628,6 +6985,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "dev": true,
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -4637,6 +7007,22 @@
"node": ">=6"
}
},
+ "node_modules/pure-rand": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
+ "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/dubzzz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fast-check"
+ }
+ ]
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -4666,6 +7052,12 @@
"safe-buffer": "^5.1.0"
}
},
+ "node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "dev": true
+ },
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
@@ -4780,6 +7172,15 @@
"node": ">=4"
}
},
+ "node_modules/resolve.exports": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz",
+ "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/restore-cursor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
@@ -4945,44 +7346,17 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/sinon": {
- "version": "19.0.2",
- "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.2.tgz",
- "integrity": "sha512-euuToqM+PjO4UgXeLETsfQiuoyPXlqFezr6YZDFwHR3t4qaX0fZUe1MfPMznTL5f8BWrVS89KduLdMUsxFCO6g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@sinonjs/commons": "^3.0.1",
- "@sinonjs/fake-timers": "^13.0.2",
- "@sinonjs/samsam": "^8.0.1",
- "diff": "^7.0.0",
- "nise": "^6.1.1",
- "supports-color": "^7.2.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/sinon"
- }
- },
- "node_modules/sinon/node_modules/diff": {
- "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"
- }
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true
},
- "node_modules/sinon/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
"engines": {
"node": ">=8"
}
@@ -5065,6 +7439,33 @@
"node": ">=0.10.0"
}
},
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "node_modules/stack-utils": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+ "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/stack-utils/node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/stdin-discarder": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz",
@@ -5100,6 +7501,40 @@
"node": ">=0.6.19"
}
},
+ "node_modules/string-length": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+ "dev": true,
+ "dependencies": {
+ "char-regex": "^1.0.2",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/string-length/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-length/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -5462,6 +7897,12 @@
"node": "*"
}
},
+ "node_modules/tmpl": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
+ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+ "dev": true
+ },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -5487,6 +7928,66 @@
"typescript": ">=4.8.4"
}
},
+ "node_modules/ts-jest": {
+ "version": "29.2.6",
+ "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.6.tgz",
+ "integrity": "sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==",
+ "dev": true,
+ "dependencies": {
+ "bs-logger": "^0.2.6",
+ "ejs": "^3.1.10",
+ "fast-json-stable-stringify": "^2.1.0",
+ "jest-util": "^29.0.0",
+ "json5": "^2.2.3",
+ "lodash.memoize": "^4.1.2",
+ "make-error": "^1.3.6",
+ "semver": "^7.7.1",
+ "yargs-parser": "^21.1.1"
+ },
+ "bin": {
+ "ts-jest": "cli.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": ">=7.0.0-beta.0 <8",
+ "@jest/transform": "^29.0.0",
+ "@jest/types": "^29.0.0",
+ "babel-jest": "^29.0.0",
+ "jest": "^29.0.0",
+ "typescript": ">=4.3 <6"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "@jest/transform": {
+ "optional": true
+ },
+ "@jest/types": {
+ "optional": true
+ },
+ "babel-jest": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ts-jest/node_modules/semver": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/ts-loader": {
"version": "9.5.2",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz",
@@ -5508,6 +8009,62 @@
"webpack": "^5.0.0"
}
},
+ "node_modules/ts-node": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ts-node/node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -5537,6 +8094,18 @@
"node": ">=4"
}
},
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/typescript": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
@@ -5615,6 +8184,14 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
"node_modules/v8-to-istanbul": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
@@ -5630,6 +8207,15 @@
"node": ">=10.12.0"
}
},
+ "node_modules/walker": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
+ "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+ "dev": true,
+ "dependencies": {
+ "makeerror": "1.0.12"
+ }
+ },
"node_modules/watchpack": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
@@ -5933,6 +8519,25 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/write-file-atomic": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
+ "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
+ "dev": true,
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.7"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/write-file-atomic/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
@@ -5963,6 +8568,12 @@
"node": ">=10"
}
},
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
"node_modules/yaml": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
@@ -6065,6 +8676,17 @@
"node": ">=8"
}
},
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index d2854ba..f1d5df8 100644
--- a/package.json
+++ b/package.json
@@ -158,18 +158,32 @@
]
}
},
+ "directories": {
+ "src": "./src",
+ "test": "./test"
+ },
"scripts": {
"vscode:prepublish": "npm run package",
"compile": "webpack",
+ "test": "jest",
+ "test:watch": "jest --watchAll --no-cache",
"watch": "webpack --watch",
"package": "webpack --mode production --devtool hidden-source-map",
"compile-tests": "tsc -p . --outDir out",
"watch-tests": "tsc -p . -w --outDir out",
- "pretest": "npm run compile-tests && npm run compile && npm run lint",
"lint": "eslint src",
- "test": "vscode-test",
"prepare": "husky"
},
+ "jest": {
+ "preset": "ts-jest",
+ "testEnvironment": "node",
+ "setupFilesAfterEnv": [
+ "./test/setup.ts"
+ ],
+ "moduleNameMapper": {
+ "^vscode$": "/test/mocks/vscode-mock.ts"
+ }
+ },
"lint-staged": {
"src/**/*.ts": [
"eslint --fix",
@@ -177,29 +191,26 @@
]
},
"devDependencies": {
- "@types/chai": "^5.0.1",
- "@types/mocha": "^10.0.10",
+ "@types/jest": "^29.5.14",
"@types/node": "20.x",
- "@types/sinon": "^17.0.4",
"@types/vscode": "^1.92.0",
"@types/ws": "^8.5.14",
"@typescript-eslint/eslint-plugin": "^8.24.1",
"@typescript-eslint/parser": "^8.24.1",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
- "chai": "^5.2.0",
"css-loader": "^7.1.2",
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-unused-imports": "^4.1.4",
"husky": "^9.1.7",
+ "jest": "^29.7.0",
"lint-staged": "^15.4.3",
- "mocha": "^11.1.0",
"prettier": "^3.5.2",
"prettier-plugin-tailwindcss": "^0.6.11",
- "sinon": "^19.0.2",
"style-loader": "^4.0.0",
+ "ts-jest": "^29.2.6",
"ts-loader": "^9.5.1",
"typescript": "^5.7.2",
"webpack": "^5.95.0",
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts
index 83d72ba..4808865 100644
--- a/src/utils/envConfig.ts
+++ b/src/utils/envConfig.ts
@@ -2,7 +2,7 @@ import * as dotenv from 'dotenv';
dotenv.config();
-interface EnvConfig {
+export interface EnvConfig {
SERVER_URL?: string;
SMELL_MAP_KEY?: string;
FILE_CHANGES_KEY?: string;
diff --git a/test/mocks/env-config-mock.ts b/test/mocks/env-config-mock.ts
new file mode 100644
index 0000000..f088efe
--- /dev/null
+++ b/test/mocks/env-config-mock.ts
@@ -0,0 +1,10 @@
+import { EnvConfig } from '../utils/envConfig';
+
+export const envConfig: EnvConfig = {
+ SERVER_URL: 'server-url',
+ SMELL_MAP_KEY: 'smell-map-key',
+ FILE_CHANGES_KEY: 'file-changes-key',
+ LAST_USED_SMELLS_KEY: 'last-used-smells-key',
+ CURRENT_REFACTOR_DATA_KEY: 'current-refator-data-key',
+ ACTIVE_DIFF_KEY: 'active-diff-key',
+};
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
new file mode 100644
index 0000000..66b371d
--- /dev/null
+++ b/test/mocks/vscode-mock.ts
@@ -0,0 +1,71 @@
+// test/mocks/vscode-mock.ts
+interface Config {
+ configGet: any;
+ filePath: any;
+ docText: any;
+}
+
+// Configuration object to dynamically change values during tests
+export const config: Config = {
+ configGet: { smell1: true, smell2: true },
+ filePath: 'fake.py',
+ docText: 'Mock document text',
+};
+
+// Mock for `vscode.TextEditor`
+export const TextEditor = {
+ document: {
+ getText: jest.fn(() => {
+ console.log('MOCK getText:', config.docText);
+ return config.docText;
+ }),
+ },
+};
+
+interface Window {
+ showInformationMessage: jest.Mock;
+ showErrorMessage: jest.Mock;
+ showWarningMessage: jest.Mock;
+ activeTextEditor: any;
+}
+
+export const window = {
+ showInformationMessage: jest.fn(async (message: string) => {
+ console.log('MOCK showInformationMessage:', message);
+ return message;
+ }),
+ showErrorMessage: jest.fn(async (message: string) => {
+ console.log('MOCK showErrorMessage:', message);
+ return message;
+ }),
+ showWarningMessage: jest.fn(async (message: string) => {
+ console.log('MOCK showWarningMessage:', message);
+ return message;
+ }),
+ activeTextEditor: TextEditor,
+};
+
+interface Workspace {
+ getConfiguration: jest.Mock;
+}
+
+export const workspace: Workspace = {
+ getConfiguration: jest.fn((section?: string) => ({
+ get: jest.fn((key: string, _defaultReturn: any) => {
+ console.log(`MOCK getConfiguration: ${section}.${key}`);
+ return config.configGet;
+ }),
+ })),
+};
+
+export interface Vscode {
+ window: Window;
+ workspace: Workspace;
+}
+
+const vscode: Vscode = {
+ window,
+ workspace,
+};
+
+export default vscode;
diff --git a/test/setup.ts b/test/setup.ts
new file mode 100644
index 0000000..bb487a4
--- /dev/null
+++ b/test/setup.ts
@@ -0,0 +1,3 @@
+jest.mock('vscode');
+
+jest.mock('./../src/utils/envConfig');
diff --git a/tsconfig.json b/tsconfig.json
index 1da27ad..705af40 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,17 +1,21 @@
{
"compilerOptions": {
- "module": "Node16",
- "target": "ES2022",
- "lib": [
- "ES2022",
- "DOM",
- "DOM.Iterable"
- ],
- "sourceMap": true,
- "rootDir": "src",
- "strict": true, /* enable all strict type-checking options */
- "typeRoots": ["./node_modules/@types", "./types"],
- "forceConsistentCasingInFileNames": true
+ "module": "Node16",
+ "target": "ES2022",
+ "lib": [
+ "ES2022",
+ "DOM",
+ "DOM.Iterable"
+ ],
+ "sourceMap": true,
+ "rootDirs": ["./src", "./test"],
+ "outDir": "dist",
+ "strict": true,
+ "typeRoots": ["./node_modules/@types", "./types"],
+ "forceConsistentCasingInFileNames": true,
+ "allowSyntheticDefaultImports": true
},
- "include": ["src/global.d.ts", "src/**/*", "media/script.js"]
-}
+ "include": ["./src/global.d.ts", "./src/types.d.ts", "src/**/*.ts", "test/**/*.ts", "media/script.js"],
+ "exclude": ["node_modules", "dist"]
+ }
+
\ No newline at end of file
From 00db1197c3778550e47a4861030f486e1d3d7468 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 1 Mar 2025 00:49:00 -0500
Subject: [PATCH 53/83] adjusted global type paths
---
src/api/backend.ts | 1 -
src/commands/detectSmells.ts | 1 -
src/global.d.ts | 35 ++++++++++++++++++++++-----------
src/{types.ts => types.d.ts} | 0
src/ui/hoverManager.ts | 1 -
src/ui/refactorView.ts | 1 -
src/utils/handleEditorChange.ts | 1 -
src/utils/serverStatus.ts | 2 +-
src/utils/smellDetails.ts | 2 --
9 files changed, 25 insertions(+), 19 deletions(-)
rename src/{types.ts => types.d.ts} (100%)
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 23e9785..beb3af6 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,6 +1,5 @@
import * as vscode from 'vscode';
-import { Smell } from '../types';
import { envConfig } from '../utils/envConfig';
import { serverStatus } from '../utils/serverStatus';
import { ServerStatusType } from '../utils/serverStatus';
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 4049007..f644f1f 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -7,7 +7,6 @@ import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
import { hashContent, updateHash } from '../utils/hashDocs';
import { wipeWorkCache } from './wipeWorkCache'; // β
Import cache wipe function
-import { Smell } from '../types';
import { serverStatus, ServerStatusType } from '../utils/serverStatus';
let serverOn: boolean = true;
diff --git a/src/global.d.ts b/src/global.d.ts
index 07104d3..72c878a 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -5,14 +5,14 @@ export {};
declare global {
// Define your global types here
- interface Occurrence {
+ export interface Occurrence {
line: number;
endLine?: number;
column: number;
endColumn?: number;
}
-
- interface AdditionalInfo {
+
+ export interface AdditionalInfo {
// CRC
repetitions?: number;
callString?: string;
@@ -20,8 +20,8 @@ declare global {
concatTarget?: string;
innerLoopLine?: number;
}
-
- interface Smell {
+
+ export interface Smell {
type: string; // Type of the smell (e.g., "performance", "convention")
symbol: string; // Symbolic identifier for the smell (e.g., "cached-repeated-calls")
message: string; // Detailed description of the smell
@@ -33,21 +33,34 @@ declare global {
occurences: Occurrence[]; // Optional: List of occurrences for repeated calls
additionalInfo: AdditionalInfo;
}
-
- interface ChangedFile {
+
+ export interface ChangedFile {
original: string;
refactored: string;
}
-
- interface RefactoredData {
+
+ export interface RefactoredData {
tempDir: string;
targetFile: ChangedFile;
energySaved: number;
affectedFiles: ChangedFile[];
}
-
- interface RefactorOutput {
+
+ export interface RefactorOutput {
refactoredData?: RefactoredData; // Refactored code as a string
updatedSmells: Smell[]; //
}
+
+ export interface ActiveDiff {
+ files: ChangedFile[];
+ isOpen: boolean;
+ firstOpen: boolean;
+ }
+
+ export type SmellDetails = {
+ symbol: string;
+ message: string;
+ colour: string; // RGB colour as a string
+ };
+
}
diff --git a/src/types.ts b/src/types.d.ts
similarity index 100%
rename from src/types.ts
rename to src/types.d.ts
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index af1a2dc..aab4891 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -1,5 +1,4 @@
import * as vscode from 'vscode';
-import { Smell } from '../types';
import {
refactorSelectedSmell,
refactorAllSmellsOfType,
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
index c952c2c..9b3faf5 100644
--- a/src/ui/refactorView.ts
+++ b/src/ui/refactorView.ts
@@ -4,7 +4,6 @@ import * as fs from 'fs';
import { envConfig } from '../utils/envConfig';
import { readFileSync } from 'fs';
-import { ActiveDiff } from '../types';
import { sidebarState } from '../utils/handleEditorChange';
import { MultiRefactoredData } from '../commands/refactorSmell';
diff --git a/src/utils/handleEditorChange.ts b/src/utils/handleEditorChange.ts
index 6e82836..fd70431 100644
--- a/src/utils/handleEditorChange.ts
+++ b/src/utils/handleEditorChange.ts
@@ -3,7 +3,6 @@ import { setTimeout } from 'timers/promises';
import { envConfig } from './envConfig';
import { ContextManager } from '../context/contextManager';
-import { ActiveDiff } from '../types';
interface DiffInfo {
original: vscode.Uri;
diff --git a/src/utils/serverStatus.ts b/src/utils/serverStatus.ts
index 80aa45e..752f410 100644
--- a/src/utils/serverStatus.ts
+++ b/src/utils/serverStatus.ts
@@ -23,7 +23,7 @@ class ServerStatus extends EventEmitter {
);
}
} else {
- vscode.window.showWarningMessage('Server connection lost.');
+ vscode.window.showWarningMessage("Can't connect to ecooptimizer server.");
}
this.status = newStatus;
this.emit('change', newStatus); // Notify listeners
diff --git a/src/utils/smellDetails.ts b/src/utils/smellDetails.ts
index bff6a20..d6b46d5 100644
--- a/src/utils/smellDetails.ts
+++ b/src/utils/smellDetails.ts
@@ -1,5 +1,3 @@
-import { SmellDetails } from '../types';
-
export const SMELL_MAP: Map = new Map([
[
'R1729', // id: "use-a-generator"
From 461f6fd9560939c6f553fc92e7bc662d79e45c86 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 1 Mar 2025 00:51:38 -0500
Subject: [PATCH 54/83] added tests for smellDetection module
ssm-lab/capstone--source-code-optimizer#380
---
src/commands/__test__/detect-smells.test.ts | 231 ++++++++++++++++++++
src/commands/detectSmells.ts | 77 ++-----
2 files changed, 252 insertions(+), 56 deletions(-)
create mode 100644 src/commands/__test__/detect-smells.test.ts
diff --git a/src/commands/__test__/detect-smells.test.ts b/src/commands/__test__/detect-smells.test.ts
new file mode 100644
index 0000000..f074759
--- /dev/null
+++ b/src/commands/__test__/detect-smells.test.ts
@@ -0,0 +1,231 @@
+// test/detect-smells.test.ts
+import { ContextManager } from '../../context/contextManager';
+import { FileHighlighter } from '../../ui/fileHighlighter';
+
+import vscode, { config } from './../../../test/mocks/vscode-mock';
+
+import * as backend from '../../api/backend';
+import * as hashDocs from '../../utils/hashDocs';
+import * as editorUtils from '../../utils/editorUtils';
+
+import { detectSmells } from '../detectSmells';
+import { serverStatus, ServerStatusType } from '../../utils/serverStatus';
+import { wipeWorkCache } from '../wipeWorkCache';
+
+jest.mock('./../wipeWorkCache', () => ({
+ wipeWorkCache: jest.fn(),
+}));
+
+describe('detectSmells', () => {
+ let contextManagerMock: ContextManager;
+
+ beforeEach(() => {
+ // Reset all mocks before each test
+ jest.clearAllMocks();
+
+ // Mock ContextManager
+ contextManagerMock = {
+ getWorkspaceData: jest.fn(),
+ setWorkspaceData: jest.fn(),
+ } as unknown as ContextManager;
+ });
+
+ it('should show an error if no active editor is found', async () => {
+ jest
+ .spyOn(editorUtils, 'getEditorAndFilePath')
+ .mockReturnValue({ editor: undefined, filePath: undefined });
+
+ await detectSmells(contextManagerMock);
+
+ // Assert error message was shown
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: No active editor found.',
+ );
+ });
+
+ it('should show an error if no file path is found', async () => {
+ jest.spyOn(editorUtils, 'getEditorAndFilePath').mockReturnValue({
+ editor: vscode.window.activeTextEditor,
+ filePath: undefined,
+ });
+
+ await detectSmells(contextManagerMock);
+
+ // Assert error message was shown
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: Active editor has no valid file path.',
+ );
+ });
+
+ it('should show a warning if no smells are enabled', async () => {
+ jest.spyOn(editorUtils, 'getEditorAndFilePath').mockReturnValueOnce({
+ editor: vscode.window.activeTextEditor,
+ filePath: 'fake.path',
+ });
+
+ jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
+ get: jest.fn().mockReturnValue({ smell1: false, smell2: false }),
+ });
+
+ await detectSmells(contextManagerMock);
+
+ // Assert warning message was shown
+ expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(
+ 'Eco: No smells are enabled! Detection skipped.',
+ );
+ });
+
+ it('should use cached smells when hash is unchanged, same smells enabled', async () => {
+ jest.spyOn(editorUtils, 'getEditorAndFilePath').mockReturnValueOnce({
+ editor: vscode.window.activeTextEditor,
+ filePath: 'fake.path',
+ });
+
+ jest.spyOn(hashDocs, 'hashContent').mockReturnValue('someHash');
+
+ jest
+ .spyOn(contextManagerMock, 'getWorkspaceData')
+ .mockReturnValueOnce(config.configGet)
+ .mockReturnValueOnce({
+ 'fake.path': {
+ hash: 'someHash',
+ smells: [],
+ },
+ });
+
+ jest.spyOn(serverStatus, 'getStatus').mockReturnValue(ServerStatusType.UP);
+
+ await detectSmells(contextManagerMock);
+
+ expect(vscode.window.showInformationMessage).toHaveBeenNthCalledWith(
+ 1,
+ 'Eco: Using cached smells for fake.path',
+ );
+ });
+
+ it('should fetch new smells on changed enabled smells', async () => {
+ jest.spyOn(editorUtils, 'getEditorAndFilePath').mockReturnValueOnce({
+ editor: vscode.window.activeTextEditor,
+ filePath: 'fake.path',
+ });
+
+ jest.spyOn(hashDocs, 'hashContent').mockReturnValue('someHash');
+ jest.spyOn(hashDocs, 'updateHash').mockResolvedValue();
+
+ jest
+ .spyOn(contextManagerMock, 'getWorkspaceData')
+ .mockReturnValueOnce({ smell1: true, smell2: false })
+ .mockReturnValueOnce({});
+
+ jest.spyOn(backend, 'fetchSmells').mockResolvedValueOnce([]);
+
+ jest.spyOn(serverStatus, 'getStatus').mockReturnValue(ServerStatusType.UP);
+
+ // Simulate no smells enabled
+ // config.configGet = { smell1: false, smell2: false };
+
+ await detectSmells(contextManagerMock);
+
+ expect(wipeWorkCache).toHaveBeenCalled();
+ expect(hashDocs.updateHash).toHaveBeenCalled();
+ expect(backend.fetchSmells).toHaveBeenCalled();
+ expect(contextManagerMock.setWorkspaceData).toHaveBeenCalledTimes(2);
+ });
+
+ it('should fetch new smells hash change, same enabled smells', async () => {
+ jest.spyOn(editorUtils, 'getEditorAndFilePath').mockReturnValueOnce({
+ editor: vscode.window.activeTextEditor,
+ filePath: 'fake.path',
+ });
+
+ jest.spyOn(hashDocs, 'hashContent').mockReturnValue('someHash');
+ jest.spyOn(hashDocs, 'updateHash').mockResolvedValue();
+
+ jest
+ .spyOn(contextManagerMock, 'getWorkspaceData')
+ .mockReturnValueOnce(config.configGet)
+ .mockReturnValueOnce({
+ 'fake.path': {
+ hash: 'differentHash',
+ smells: [],
+ },
+ });
+
+ jest.spyOn(serverStatus, 'getStatus').mockReturnValue(ServerStatusType.UP);
+
+ jest.spyOn(backend, 'fetchSmells').mockResolvedValueOnce([]);
+
+ // Simulate no smells enabled
+ // config.configGet = { smell1: false, smell2: false };
+
+ await detectSmells(contextManagerMock);
+
+ expect(hashDocs.updateHash).toHaveBeenCalled();
+ expect(backend.fetchSmells).toHaveBeenCalled();
+ expect(contextManagerMock.setWorkspaceData).toHaveBeenCalledTimes(1);
+ });
+
+ it('should return if no cached smells and server down', async () => {
+ jest.spyOn(editorUtils, 'getEditorAndFilePath').mockReturnValueOnce({
+ editor: vscode.window.activeTextEditor,
+ filePath: 'fake.path',
+ });
+
+ jest.spyOn(hashDocs, 'hashContent').mockReturnValue('someHash');
+ jest.spyOn(hashDocs, 'updateHash').mockResolvedValue();
+
+ jest
+ .spyOn(contextManagerMock, 'getWorkspaceData')
+ .mockReturnValueOnce(config.configGet)
+ .mockReturnValueOnce({});
+
+ jest.spyOn(serverStatus, 'getStatus').mockReturnValue(ServerStatusType.DOWN);
+
+ await detectSmells(contextManagerMock);
+
+ expect(vscode.window.showWarningMessage).toHaveBeenLastCalledWith(
+ 'Action blocked: Server is down and no cached smells exist for this file version.',
+ );
+ });
+
+ it('should highlight smells if smells are found', async () => {
+ jest.spyOn(editorUtils, 'getEditorAndFilePath').mockReturnValueOnce({
+ editor: vscode.window.activeTextEditor,
+ filePath: 'fake.path',
+ });
+
+ jest.spyOn(hashDocs, 'hashContent').mockReturnValue('someHash');
+
+ jest
+ .spyOn(contextManagerMock, 'getWorkspaceData')
+ .mockReturnValueOnce(config.configGet)
+ .mockReturnValueOnce({
+ 'fake.path': {
+ hash: 'someHash',
+ smells: [{} as unknown as Smell],
+ },
+ });
+
+ jest.spyOn(serverStatus, 'getStatus').mockReturnValue(ServerStatusType.UP);
+
+ const mockHighlightSmells = jest.fn();
+ jest
+ .spyOn(FileHighlighter.prototype, 'highlightSmells')
+ .mockImplementation(mockHighlightSmells);
+
+ await detectSmells(contextManagerMock);
+
+ expect(vscode.window.showInformationMessage).toHaveBeenCalledTimes(2);
+ expect(vscode.window.showInformationMessage).toHaveBeenNthCalledWith(
+ 1,
+ 'Eco: Using cached smells for fake.path',
+ );
+
+ expect(vscode.window.showInformationMessage).toHaveBeenNthCalledWith(
+ 2,
+ 'Eco: Highlighted 1 smells.',
+ );
+
+ expect(mockHighlightSmells).toHaveBeenCalled();
+ });
+});
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index f644f1f..b8d2f0c 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -9,17 +9,12 @@ import { hashContent, updateHash } from '../utils/hashDocs';
import { wipeWorkCache } from './wipeWorkCache'; // β
Import cache wipe function
import { serverStatus, ServerStatusType } from '../utils/serverStatus';
-let serverOn: boolean = true;
-
serverStatus.on('change', (newStatus: ServerStatusType) => {
console.log('Server status changed:', newStatus);
if (newStatus === ServerStatusType.DOWN) {
- serverOn = false;
vscode.window.showWarningMessage(
'Smell detection limited. Only cached smells will be shown.',
);
- } else {
- serverOn = true;
}
});
@@ -49,17 +44,11 @@ export async function detectSmells(contextManager: ContextManager): Promise enabledSmells[smell],
- );
-
- if (activeSmells.length === 0) {
+ if (!Object.values(enabledSmells).includes(true)) {
vscode.window.showWarningMessage(
'Eco: No smells are enabled! Detection skipped.',
);
- console.warn('Eco: No smells are enabled. Detection will not proceed.');
return;
}
@@ -74,68 +63,44 @@ export async function detectSmells(contextManager: ContextManager): Promise =
contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!) || {};
-
const fileSmells = allSmells[filePath];
const currentFileHash = hashContent(editor.document.getText());
- // β
Function to fetch smells and update cache
- async function fetchAndStoreSmells(): Promise {
- console.log(
- `Eco: Fetching smells from backend for file: ${filePath} with filters: ${activeSmells}`,
- );
-
- if (!filePath) {
- console.error(`Eco: File path is undefined when fetching smells.`);
- return undefined;
- }
-
- const smellsData = await fetchSmells(filePath, activeSmells);
-
- if (!smellsData || smellsData.length === 0) {
- console.log(`Eco: No smells found in file: ${filePath}`);
- vscode.window.showInformationMessage('Eco: No code smells detected.');
- return [];
- }
-
- console.log(
- `Eco: ${smellsData.length} smells found in ${filePath}. Updating cache.`,
- );
-
- // β
Ensure safe update of smells cache
- allSmells[filePath] = { hash: currentFileHash, smells: smellsData };
- contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, allSmells);
-
- return smellsData;
- }
-
let smellsData: Smell[] | undefined;
- // β
**Check cache before requesting backend**
if (fileSmells && currentFileHash === fileSmells.hash) {
- console.log(`Eco: Using cached smells for ${filePath}`);
vscode.window.showInformationMessage(`Eco: Using cached smells for ${filePath}`);
+
smellsData = fileSmells.smells;
- } else if (serverOn) {
- if (fileSmells) {
- console.log(`Eco: File changed. Updating smells.`);
- } else {
- console.log(`Eco: No cached smells found. Fetching from backend.`);
+ } else if (serverStatus.getStatus() === ServerStatusType.UP) {
+ updateHash(contextManager, editor.document);
+
+ try {
+ smellsData = await fetchSmells(
+ filePath,
+ Object.keys(enabledSmells).filter((s) => enabledSmells[s]),
+ );
+ } catch (err) {
+ console.error(err);
+ return;
}
- updateHash(contextManager, editor.document);
- smellsData = await fetchAndStoreSmells();
+ if (smellsData) {
+ allSmells[filePath] = { hash: currentFileHash, smells: smellsData };
+ contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, allSmells);
+ }
} else {
vscode.window.showWarningMessage(
- 'Action blocked: Server is down and no cached smells exists for this file version.',
+ 'Action blocked: Server is down and no cached smells exist for this file version.',
);
return;
}
if (!smellsData || smellsData.length === 0) {
- console.log(`Eco: No smells to highlight for ${filePath}.`);
+ vscode.window.showInformationMessage('Eco: No code smells detected.');
return;
}
@@ -150,6 +115,6 @@ export async function detectSmells(contextManager: ContextManager): Promise
Date: Sat, 1 Mar 2025 11:56:28 -0500
Subject: [PATCH 55/83] ssm-lab/capstone--source-code-optimizer#385 Add unit
tests for wipeWorkCache
- Implemented unit tests for wipeWorkCache.ts
- Created a mock for ContextManager to support testing
- Updated VS Code mock to include visibleTextEditors
- Restructured the unit test directory for better organization under commands/
---
src/commands/wipeWorkCache.ts | 4 +-
.../commands/detectSmells.test.ts | 20 +--
test/commands/wipeWorkCache.test.ts | 123 ++++++++++++++++++
test/mocks/contextManager-mock.ts | 53 ++++++++
test/mocks/vscode-mock.ts | 2 +
tests/commands/testWipeWorkCache.ts | 0
6 files changed, 190 insertions(+), 12 deletions(-)
rename src/commands/__test__/detect-smells.test.ts => test/commands/detectSmells.test.ts (91%)
create mode 100644 test/commands/wipeWorkCache.test.ts
create mode 100644 test/mocks/contextManager-mock.ts
delete mode 100644 tests/commands/testWipeWorkCache.ts
diff --git a/src/commands/wipeWorkCache.ts b/src/commands/wipeWorkCache.ts
index eefad39..345a723 100644
--- a/src/commands/wipeWorkCache.ts
+++ b/src/commands/wipeWorkCache.ts
@@ -10,14 +10,14 @@ export async function wipeWorkCache(
try {
console.log('Eco: Wiping workspace cache...');
- // β
Clear stored smells cache
+ // Clear stored smells cache
await contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, {});
if (reason === 'manual') {
await contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, {});
}
- // β
Update file hashes for all open editors
+ // Update file hashes for all open editors
const visibleEditors = vscode.window.visibleTextEditors;
if (visibleEditors.length === 0) {
diff --git a/src/commands/__test__/detect-smells.test.ts b/test/commands/detectSmells.test.ts
similarity index 91%
rename from src/commands/__test__/detect-smells.test.ts
rename to test/commands/detectSmells.test.ts
index f074759..e76b3eb 100644
--- a/src/commands/__test__/detect-smells.test.ts
+++ b/test/commands/detectSmells.test.ts
@@ -1,18 +1,18 @@
// test/detect-smells.test.ts
-import { ContextManager } from '../../context/contextManager';
-import { FileHighlighter } from '../../ui/fileHighlighter';
+import { ContextManager } from '../../src/context/contextManager';
+import { FileHighlighter } from '../../src/ui/fileHighlighter';
-import vscode, { config } from './../../../test/mocks/vscode-mock';
+import vscode, { config } from '../mocks/vscode-mock';
-import * as backend from '../../api/backend';
-import * as hashDocs from '../../utils/hashDocs';
-import * as editorUtils from '../../utils/editorUtils';
+import * as backend from '../../src/api/backend';
+import * as hashDocs from '../../src/utils/hashDocs';
+import * as editorUtils from '../../src/utils/editorUtils';
-import { detectSmells } from '../detectSmells';
-import { serverStatus, ServerStatusType } from '../../utils/serverStatus';
-import { wipeWorkCache } from '../wipeWorkCache';
+import { detectSmells } from '../../src/commands/detectSmells';
+import { serverStatus, ServerStatusType } from '../../src/utils/serverStatus';
+import { wipeWorkCache } from '../../src/commands/wipeWorkCache';
-jest.mock('./../wipeWorkCache', () => ({
+jest.mock('../../src/commands/wipeWorkCache', () => ({
wipeWorkCache: jest.fn(),
}));
diff --git a/test/commands/wipeWorkCache.test.ts b/test/commands/wipeWorkCache.test.ts
new file mode 100644
index 0000000..a2689f1
--- /dev/null
+++ b/test/commands/wipeWorkCache.test.ts
@@ -0,0 +1,123 @@
+import mockContextManager from '../mocks/contextManager-mock';
+import { wipeWorkCache } from '../../src/commands/wipeWorkCache';
+import vscode from '../mocks/vscode-mock';
+import { envConfig } from '../mocks/env-config-mock';
+import { updateHash } from '../../src/utils/hashDocs';
+
+// mock updateHash function
+jest.mock('../../src/utils/hashDocs', () => ({
+ updateHash: jest.fn(),
+}));
+
+// mock environment config
+jest.mock('../../src/utils/envConfig', () => ({
+ envConfig: {
+ SMELL_MAP_KEY: 'smell-map-key',
+ FILE_CHANGES_KEY: 'file-changes-key',
+ },
+}));
+
+describe('wipeWorkCache', () => {
+ beforeEach(() => {
+ jest.clearAllMocks(); // reset mocks before each test
+ });
+
+ test('should clear stored smells cache with no reason provided', async () => {
+ // call wipeWorkCache with contextManagerMock
+ await wipeWorkCache(mockContextManager);
+
+ expect(mockContextManager.setWorkspaceData).toHaveBeenCalledWith(
+ envConfig.SMELL_MAP_KEY,
+ {},
+ );
+ expect(mockContextManager.setWorkspaceData).toHaveBeenCalledTimes(1); // only the smells cache should be cleared when no reason is provided
+ });
+
+ test('should clear stored smells cache when reason is settings', async () => {
+ // call wipeWorkCache with contextManagerMock
+ await wipeWorkCache(mockContextManager, 'settings');
+
+ expect(mockContextManager.setWorkspaceData).toHaveBeenCalledWith(
+ envConfig.SMELL_MAP_KEY,
+ {},
+ );
+ expect(mockContextManager.setWorkspaceData).toHaveBeenCalledTimes(1); // only the smells cache should be cleared when reason is settings
+ });
+
+ test('should clear file changes when reason is manual', async () => {
+ // call wipeWorkCache with contextManagerMock
+ await wipeWorkCache(mockContextManager, 'manual');
+
+ expect(mockContextManager.setWorkspaceData).toHaveBeenCalledWith(
+ envConfig.SMELL_MAP_KEY,
+ {},
+ );
+ expect(mockContextManager.setWorkspaceData).toHaveBeenCalledWith(
+ envConfig.FILE_CHANGES_KEY,
+ {},
+ );
+ expect(mockContextManager.setWorkspaceData).toHaveBeenCalledTimes(2); // both caches should be cleared when reason is manul
+ });
+
+ test('should log when there are no visible text editors', async () => {
+ vscode.window.visibleTextEditors = []; // simulate no open editors
+
+ const consoleSpy = jest.spyOn(console, 'log');
+ await wipeWorkCache(mockContextManager);
+
+ expect(consoleSpy).toHaveBeenCalledWith('Eco: No open files to update hash.');
+ });
+
+ test('should update hashes for visible text editors', async () => {
+ vscode.window.visibleTextEditors = [
+ {
+ document: { fileName: 'file1.py', getText: jest.fn(() => 'file1 content') },
+ } as any,
+ {
+ document: { fileName: 'file2.py', getText: jest.fn(() => 'file2 content') },
+ } as any,
+ ];
+
+ await wipeWorkCache(mockContextManager);
+ expect(updateHash).toHaveBeenCalledTimes(2); // should call updateHash for each open document
+ });
+ test('should display the correct message for default wipe', async () => {
+ await wipeWorkCache(mockContextManager);
+
+ expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
+ 'Eco: Successfully wiped workspace cache! β
',
+ );
+ });
+
+ test('should display the correct message when reason is "settings"', async () => {
+ await wipeWorkCache(mockContextManager, 'settings');
+
+ expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
+ 'Eco: Smell detection settings changed. Cache wiped to apply updates. β
',
+ );
+ });
+
+ test('should display the correct message when reason is "manual"', async () => {
+ await wipeWorkCache(mockContextManager, 'manual');
+
+ expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
+ 'Eco: Workspace cache manually wiped by user. β
',
+ );
+ });
+
+ test('should handle errors and display an error message', async () => {
+ mockContextManager.setWorkspaceData.mockRejectedValue(new Error('Mocked Error'));
+
+ const consoleErrorSpy = jest.spyOn(console, 'error');
+
+ await wipeWorkCache(mockContextManager);
+
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
+ 'Eco: Error while wiping workspace cache:',
+ expect.any(Error),
+ );
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: Failed to wipe workspace cache. See console for details.',
+ );
+ });
+});
diff --git a/test/mocks/contextManager-mock.ts b/test/mocks/contextManager-mock.ts
new file mode 100644
index 0000000..745d9a6
--- /dev/null
+++ b/test/mocks/contextManager-mock.ts
@@ -0,0 +1,53 @@
+// test/mocks/contextManager-mock.ts
+import * as vscode from 'vscode';
+
+interface ContextStorage {
+ globalState: Record;
+ workspaceState: Record;
+}
+
+const contextStorage: ContextStorage = {
+ globalState: {},
+ workspaceState: {},
+};
+
+const mockExtensionContext: Partial = {
+ globalState: {
+ get: jest.fn((key: string, defaultVal?: any) => {
+ console.log(`MOCK getGlobalData: ${key}`);
+ return contextStorage.globalState[key] ?? defaultVal;
+ }),
+ update: jest.fn(async (key: string, value: any) => {
+ console.log(`MOCK setGlobalData: ${key}:${value}`);
+ contextStorage.globalState[key] = value;
+ }),
+ } as any, // Casting to `any` to satisfy `vscode.ExtensionContext`
+ workspaceState: {
+ get: jest.fn((key: string, defaultVal?: any) => {
+ console.log(`MOCK getWorkspaceData: ${key}`);
+ return contextStorage.workspaceState[key] ?? defaultVal;
+ }),
+ update: jest.fn(async (key: string, value: any) => {
+ console.log(`MOCK setWorkspaceData ${key}:${value}`);
+ contextStorage.workspaceState[key] = value;
+ }),
+ } as any, // Casting to `any` to satisfy `vscode.ExtensionContext`
+};
+
+const mockContextManager = {
+ context: mockExtensionContext as vscode.ExtensionContext,
+ getGlobalData: jest.fn((key: string, defaultVal?: any) => {
+ return contextStorage.globalState[key] ?? defaultVal;
+ }),
+ setGlobalData: jest.fn(async (key: string, value: any) => {
+ contextStorage.globalState[key] = value;
+ }),
+ getWorkspaceData: jest.fn((key: string, defaultVal?: any) => {
+ return contextStorage.workspaceState[key] ?? defaultVal;
+ }),
+ setWorkspaceData: jest.fn(async (key: string, value: any) => {
+ contextStorage.workspaceState[key] = value;
+ }),
+};
+
+export default mockContextManager;
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
index 66b371d..4028e43 100644
--- a/test/mocks/vscode-mock.ts
+++ b/test/mocks/vscode-mock.ts
@@ -27,6 +27,7 @@ interface Window {
showErrorMessage: jest.Mock;
showWarningMessage: jest.Mock;
activeTextEditor: any;
+ visibleTextEditors: any[];
}
export const window = {
@@ -43,6 +44,7 @@ export const window = {
return message;
}),
activeTextEditor: TextEditor,
+ visibleTextEditors: [],
};
interface Workspace {
diff --git a/tests/commands/testWipeWorkCache.ts b/tests/commands/testWipeWorkCache.ts
deleted file mode 100644
index e69de29..0000000
From 1238f9803c94185b3238984b8cddc8cf5849bb95 Mon Sep 17 00:00:00 2001
From: tbrar06
Date: Sat, 1 Mar 2025 14:43:14 -0500
Subject: [PATCH 56/83] Centralized env config mocking
---
test/commands/wipeWorkCache.test.ts | 8 --------
test/mocks/env-config-mock.ts | 24 +++++++++++++++---------
2 files changed, 15 insertions(+), 17 deletions(-)
diff --git a/test/commands/wipeWorkCache.test.ts b/test/commands/wipeWorkCache.test.ts
index a2689f1..38ff42e 100644
--- a/test/commands/wipeWorkCache.test.ts
+++ b/test/commands/wipeWorkCache.test.ts
@@ -9,14 +9,6 @@ jest.mock('../../src/utils/hashDocs', () => ({
updateHash: jest.fn(),
}));
-// mock environment config
-jest.mock('../../src/utils/envConfig', () => ({
- envConfig: {
- SMELL_MAP_KEY: 'smell-map-key',
- FILE_CHANGES_KEY: 'file-changes-key',
- },
-}));
-
describe('wipeWorkCache', () => {
beforeEach(() => {
jest.clearAllMocks(); // reset mocks before each test
diff --git a/test/mocks/env-config-mock.ts b/test/mocks/env-config-mock.ts
index f088efe..48aad7b 100644
--- a/test/mocks/env-config-mock.ts
+++ b/test/mocks/env-config-mock.ts
@@ -1,10 +1,16 @@
-import { EnvConfig } from '../utils/envConfig';
+import { envConfig, EnvConfig } from '../../src/utils/envConfig';
-export const envConfig: EnvConfig = {
- SERVER_URL: 'server-url',
- SMELL_MAP_KEY: 'smell-map-key',
- FILE_CHANGES_KEY: 'file-changes-key',
- LAST_USED_SMELLS_KEY: 'last-used-smells-key',
- CURRENT_REFACTOR_DATA_KEY: 'current-refator-data-key',
- ACTIVE_DIFF_KEY: 'active-diff-key',
-};
+jest.mock('../../src/utils/envConfig', () => {
+ const mockEnvConfig: EnvConfig = {
+ SERVER_URL: 'server-url',
+ SMELL_MAP_KEY: 'smell-map-key',
+ FILE_CHANGES_KEY: 'file-changes-key',
+ LAST_USED_SMELLS_KEY: 'last-used-smells-key',
+ CURRENT_REFACTOR_DATA_KEY: 'current-refactor-data-key',
+ ACTIVE_DIFF_KEY: 'active-diff-key',
+ };
+
+ return { envConfig: mockEnvConfig };
+});
+
+export { envConfig };
From c53bc6c16a5dedb1007052a0a3becb25ffd44a1d Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Sat, 1 Mar 2025 15:13:35 -0500
Subject: [PATCH 57/83] Have 2/3 test cases working for hoverManager (#396)
---
src/ui/hoverManager.ts | 13 ++--
test/mocks/vscode-mock.ts | 62 ++++++++++++++--
test/ui/hoverManager.test.ts | 132 +++++++++++++++++++++++++++++++++++
3 files changed, 198 insertions(+), 9 deletions(-)
create mode 100644 test/ui/hoverManager.test.ts
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index aab4891..09df83e 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -20,7 +20,7 @@ export class HoverManager {
return HoverManager.instance;
}
- private constructor(
+ public constructor(
private contextManager: ContextManager,
smells: Smell[],
) {
@@ -30,12 +30,12 @@ export class HoverManager {
this.registerCommands();
}
- private updateSmells(smells: Smell[]): void {
+ public updateSmells(smells: Smell[]): void {
this.smells = smells || [];
}
// Register hover provider for Python files
- private registerHoverProvider(): void {
+ public registerHoverProvider(): void {
this.vscodeContext.subscriptions.push(
vscode.languages.registerHoverProvider(
{ scheme: 'file', language: 'python' },
@@ -55,7 +55,7 @@ export class HoverManager {
position: vscode.Position,
): vscode.MarkdownString | null {
const lineNumber = position.line + 1; // convert to 1-based index
-
+ console.log('line number: ' + position.line);
// filter to find the smells on current line
const smellsOnLine = this.smells.filter((smell) =>
smell.occurences.some(
@@ -67,6 +67,8 @@ export class HoverManager {
),
);
+ console.log('smells: ' + smellsOnLine);
+
if (smellsOnLine.length === 0) {
return null;
}
@@ -84,13 +86,14 @@ export class HoverManager {
JSON.stringify(smell),
)})\n\n`,
);
+ console.log(hoverContent);
});
return hoverContent;
}
// Register commands for refactor actions
- private registerCommands(): void {
+ public registerCommands(): void {
this.vscodeContext.subscriptions.push(
vscode.commands.registerCommand(
'extension.refactorThisSmell',
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
index 4028e43..9dca455 100644
--- a/test/mocks/vscode-mock.ts
+++ b/test/mocks/vscode-mock.ts
@@ -15,10 +15,13 @@ export const config: Config = {
// Mock for `vscode.TextEditor`
export const TextEditor = {
document: {
- getText: jest.fn(() => {
- console.log('MOCK getText:', config.docText);
- return config.docText;
- }),
+ getText: jest.fn(() => config.docText),
+ fileName: config.filePath,
+ languageId: 'python',
+ },
+ selection: {
+ start: { line: 0, character: 0 },
+ end: { line: 0, character: 0 },
},
};
@@ -60,14 +63,65 @@ export const workspace: Workspace = {
})),
};
+// New mocks for hover functionality
+export const languages = {
+ registerHoverProvider: jest.fn(() => ({
+ dispose: jest.fn(),
+ })),
+};
+
+export const commands = {
+ registerCommand: jest.fn(),
+};
+
+// Mock VS Code classes
+export const Position = class MockPosition {
+ constructor(
+ public line: number,
+ public character: number,
+ ) {}
+};
+
+export class MockMarkdownString {
+ value: string;
+ isTrusted: boolean = false;
+
+ constructor() {
+ this.value = '';
+ this.isTrusted = false;
+ }
+
+ appendMarkdown(value: string): MockMarkdownString {
+ this.value += value;
+ return this;
+ }
+}
+
+export class MockHover {
+ constructor(public contents: MockMarkdownString | MockMarkdownString[]) {}
+}
+
+export const MarkdownString = MockMarkdownString;
+export const Hover = MockHover;
+
export interface Vscode {
window: Window;
workspace: Workspace;
+ languages: typeof languages;
+ commands: typeof commands;
+ Position: typeof Position;
+ MarkdownString: typeof MarkdownString;
+ Hover: typeof Hover;
}
const vscode: Vscode = {
window,
workspace,
+ languages,
+ commands,
+ Position,
+ MarkdownString,
+ Hover,
};
export default vscode;
diff --git a/test/ui/hoverManager.test.ts b/test/ui/hoverManager.test.ts
new file mode 100644
index 0000000..089482e
--- /dev/null
+++ b/test/ui/hoverManager.test.ts
@@ -0,0 +1,132 @@
+// test/hover-manager.test.ts
+import vscode from '../mocks/vscode-mock';
+import { HoverManager } from '../../src/ui/hoverManager';
+import { ContextManager } from '../../src/context/contextManager';
+import { Smell, Occurrence } from '../../src/types';
+
+jest.mock('../../src/commands/refactorSmell', () => ({
+ refactorSelectedSmell: jest.fn(),
+ refactorAllSmellsOfType: jest.fn(),
+}));
+
+// Mock the vscode module using our custom mock
+// jest.mock('vscode', () => vscode);
+
+describe('HoverManager', () => {
+ let contextManagerMock: ContextManager;
+ let mockSmells: Smell[];
+
+ const mockOccurrence: Occurrence = {
+ line: 5,
+ endLine: 7,
+ column: 1,
+ endColumn: 10,
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ contextManagerMock = {
+ context: {
+ subscriptions: [],
+ },
+ getContext: () => ({ subscriptions: [] }),
+ } as unknown as ContextManager;
+
+ mockSmells = [
+ {
+ type: 'performance',
+ symbol: 'CRS-001',
+ message: 'Cached repeated calls',
+ messageId: 'cached-repeated-calls',
+ confidence: 'HIGH',
+ path: '/test/file.py',
+ module: 'test_module',
+ occurences: [mockOccurrence],
+ additionalInfo: {},
+ },
+ ];
+ });
+
+ it('should register hover provider for Python files', () => {
+ new HoverManager(contextManagerMock, mockSmells);
+
+ expect(vscode.languages.registerHoverProvider).toHaveBeenCalledWith(
+ { scheme: 'file', language: 'python' },
+ expect.objectContaining({
+ provideHover: expect.any(Function),
+ }),
+ );
+ });
+
+ it('should generate valid hover content', () => {
+ const manager = new HoverManager(contextManagerMock, mockSmells);
+ const document = {
+ fileName: '/test/file.py',
+ getText: jest.fn(),
+ } as any;
+
+ const position = {
+ line: 4, // 0-based line number (will become line 5 in 1-based)
+ character: 0,
+ isBefore: jest.fn(),
+ isBeforeOrEqual: jest.fn(),
+ isAfter: jest.fn(),
+ isAfterOrEqual: jest.fn(),
+ translate: jest.fn(),
+ with: jest.fn(),
+ compareTo: jest.fn(),
+ isEqual: jest.fn(),
+ } as any; // Simplified type assertion since we don't need full Position type
+
+ // Mock document text for line range
+ document.getText.mockReturnValue('mock code content');
+ const content = manager.getHoverContent(document, position);
+ console.log(content);
+
+ expect(content?.value).toBeDefined(); // Check value exists
+ expect(content?.value).toContain('CRS-001');
+ expect(content?.value).toContain('Cached repeated calls');
+ expect(content).toBeInstanceOf(vscode.MarkdownString);
+ expect(content?.isTrusted).toBe(true);
+
+ // Verify basic structure for each smell
+ expect(content?.value).toContain('**CRS-001:** Cached repeated calls');
+ expect(content?.value).toMatch(
+ '/[Refactor](command:extension.refactorThisSmell?/',
+ );
+ expect(content?.value).toMatch(
+ '/---[Refactor all smells of this type...](command:extension.refactorAllSmellsOfType?/',
+ );
+
+ // Verify command parameters are properly encoded
+ const expectedSmellParam = encodeURIComponent(JSON.stringify(mockSmells[0]));
+ expect(content?.value).toContain(
+ `command:extension.refactorThisSmell?${expectedSmellParam}`,
+ );
+ expect(content?.value).toContain(
+ `command:extension.refactorAllSmellsOfType?${expectedSmellParam}`,
+ );
+
+ // Verify formatting between elements
+ expect(content?.value).toContain('\t\t'); // Verify tab separation
+ expect(content?.value).toContain('\n\n'); // Verify line breaks between smells
+
+ // // Verify empty case
+ // expect(manager.getHoverContent(document, invalidPosition)).toBeNull();
+ });
+
+ it('should register refactor commands', () => {
+ new HoverManager(contextManagerMock, mockSmells);
+
+ expect(vscode.commands.registerCommand).toHaveBeenCalledWith(
+ 'extension.refactorThisSmell',
+ expect.any(Function),
+ );
+
+ expect(vscode.commands.registerCommand).toHaveBeenCalledWith(
+ 'extension.refactorAllSmellsOfType',
+ expect.any(Function),
+ );
+ });
+});
From 3c480425ccc2335c3a196a0341d33f9c4bc67060 Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Sat, 1 Mar 2025 19:11:12 -0500
Subject: [PATCH 58/83] Finished hoverManager test cases (#396)
---
test/mocks/vscode-mock.ts | 35 ++++++++-------
test/ui/hoverManager.test.ts | 86 ++++++++++++++++++++++++++++++++----
2 files changed, 97 insertions(+), 24 deletions(-)
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
index 9dca455..83bce84 100644
--- a/test/mocks/vscode-mock.ts
+++ b/test/mocks/vscode-mock.ts
@@ -82,26 +82,31 @@ export const Position = class MockPosition {
) {}
};
-export class MockMarkdownString {
+interface MockMarkdownString {
+ appendMarkdown: jest.Mock;
value: string;
- isTrusted: boolean = false;
-
- constructor() {
- this.value = '';
- this.isTrusted = false;
- }
-
- appendMarkdown(value: string): MockMarkdownString {
- this.value += value;
- return this;
- }
+ isTrusted: boolean;
}
+// Create a constructor function mock
+export const MarkdownString = jest.fn().mockImplementation(() => {
+ return {
+ appendMarkdown: jest.fn(function (this: any, value: string) {
+ this.value += value;
+ return this;
+ }),
+ value: '',
+ isTrusted: false,
+ };
+}) as jest.Mock & {
+ prototype: MockMarkdownString;
+};
+
export class MockHover {
- constructor(public contents: MockMarkdownString | MockMarkdownString[]) {}
+ constructor(public contents: MockMarkdownString) {}
}
-export const MarkdownString = MockMarkdownString;
+// export const MarkdownString = MockMarkdownString;
export const Hover = MockHover;
export interface Vscode {
@@ -110,7 +115,6 @@ export interface Vscode {
languages: typeof languages;
commands: typeof commands;
Position: typeof Position;
- MarkdownString: typeof MarkdownString;
Hover: typeof Hover;
}
@@ -120,7 +124,6 @@ const vscode: Vscode = {
languages,
commands,
Position,
- MarkdownString,
Hover,
};
diff --git a/test/ui/hoverManager.test.ts b/test/ui/hoverManager.test.ts
index 089482e..15af64e 100644
--- a/test/ui/hoverManager.test.ts
+++ b/test/ui/hoverManager.test.ts
@@ -1,8 +1,11 @@
// test/hover-manager.test.ts
-import vscode from '../mocks/vscode-mock';
+// import vscode from '../mocks/vscode-mock';
import { HoverManager } from '../../src/ui/hoverManager';
import { ContextManager } from '../../src/context/contextManager';
import { Smell, Occurrence } from '../../src/types';
+import vscode from 'vscode';
+
+jest.mock('vscode');
jest.mock('../../src/commands/refactorSmell', () => ({
refactorSelectedSmell: jest.fn(),
@@ -59,6 +62,76 @@ describe('HoverManager', () => {
);
});
+ it('should subscribe hover provider correctly', () => {
+ const spy = jest.spyOn(contextManagerMock.context.subscriptions, 'push');
+ new HoverManager(contextManagerMock, mockSmells);
+ expect(spy).toHaveBeenCalledWith(expect.anything());
+ });
+
+ it('should return null for hover content if there are no smells', () => {
+ const manager = new HoverManager(contextManagerMock, []);
+ const document = { fileName: '/test/file.py', getText: jest.fn() } as any;
+ const position = { line: 4 } as any;
+ expect(manager.getHoverContent(document, position)).toBeNull();
+ });
+
+ it('should update smells when getInstance is called again', () => {
+ const initialSmells = [
+ {
+ type: 'performance',
+ symbol: 'CRS-001',
+ message: 'Cached repeated calls',
+ messageId: 'cached-repeated-calls',
+ confidence: 'HIGH',
+ path: '/test/file.py',
+ module: 'test_module',
+ occurences: [mockOccurrence],
+ additionalInfo: {},
+ },
+ ];
+
+ const newSmells = [
+ {
+ type: 'memory',
+ symbol: 'MEM-002',
+ message: 'Memory leak detected',
+ messageId: 'memory-leak',
+ confidence: 'MEDIUM',
+ path: '/test/file2.py',
+ module: 'test_module_2',
+ occurences: [mockOccurrence],
+ additionalInfo: {},
+ },
+ ];
+
+ const manager1 = HoverManager.getInstance(contextManagerMock, initialSmells);
+ expect(manager1['smells']).toEqual(initialSmells);
+
+ const manager2 = HoverManager.getInstance(contextManagerMock, newSmells);
+ expect(manager2['smells']).toEqual(newSmells);
+ expect(manager1).toBe(manager2); // Ensuring it's the same instance
+ });
+
+ it('should update smells correctly', () => {
+ const manager = new HoverManager(contextManagerMock, mockSmells);
+ const newSmells: Smell[] = [
+ {
+ type: 'security',
+ symbol: 'SEC-003',
+ message: 'Unsafe API usage',
+ messageId: 'unsafe-api',
+ confidence: 'HIGH',
+ path: '/test/file3.py',
+ module: 'security_module',
+ occurences: [mockOccurrence],
+ additionalInfo: {},
+ },
+ ];
+
+ manager.updateSmells(newSmells);
+ expect(manager['smells']).toEqual(newSmells);
+ });
+
it('should generate valid hover content', () => {
const manager = new HoverManager(contextManagerMock, mockSmells);
const document = {
@@ -82,23 +155,20 @@ describe('HoverManager', () => {
// Mock document text for line range
document.getText.mockReturnValue('mock code content');
const content = manager.getHoverContent(document, position);
- console.log(content);
expect(content?.value).toBeDefined(); // Check value exists
expect(content?.value).toContain('CRS-001');
expect(content?.value).toContain('Cached repeated calls');
- expect(content).toBeInstanceOf(vscode.MarkdownString);
expect(content?.isTrusted).toBe(true);
// Verify basic structure for each smell
expect(content?.value).toContain('**CRS-001:** Cached repeated calls');
- expect(content?.value).toMatch(
- '/[Refactor](command:extension.refactorThisSmell?/',
+ expect(content?.value).toContain(
+ '[Refactor](command:extension.refactorThisSmell?',
);
- expect(content?.value).toMatch(
- '/---[Refactor all smells of this type...](command:extension.refactorAllSmellsOfType?/',
+ expect(content?.value).toContain(
+ '[Refactor all smells of this type...](command:extension.refactorAllSmellsOfType?',
);
-
// Verify command parameters are properly encoded
const expectedSmellParam = encodeURIComponent(JSON.stringify(mockSmells[0]));
expect(content?.value).toContain(
From d2341350f8d92c52fd6841da02d423bc7340589d Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Sat, 1 Mar 2025 20:28:46 -0500
Subject: [PATCH 59/83] Added 4/6 passing test cases for line selection manager
(#413)
---
test/mocks/vscode-mock.ts | 9 +++
test/ui/lineSelection.test.ts | 107 ++++++++++++++++++++++++++++++++++
2 files changed, 116 insertions(+)
create mode 100644 test/ui/lineSelection.test.ts
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
index 83bce84..b09cf3d 100644
--- a/test/mocks/vscode-mock.ts
+++ b/test/mocks/vscode-mock.ts
@@ -18,17 +18,23 @@ export const TextEditor = {
getText: jest.fn(() => config.docText),
fileName: config.filePath,
languageId: 'python',
+ lineAt: jest.fn((line: number) => ({
+ text: `mock line content ${line}`,
+ })),
},
selection: {
start: { line: 0, character: 0 },
end: { line: 0, character: 0 },
+ isSingleLine: true,
},
+ setDecorations: jest.fn(),
};
interface Window {
showInformationMessage: jest.Mock;
showErrorMessage: jest.Mock;
showWarningMessage: jest.Mock;
+ createTextEditorDecorationType: jest.Mock;
activeTextEditor: any;
visibleTextEditors: any[];
}
@@ -46,6 +52,9 @@ export const window = {
console.log('MOCK showWarningMessage:', message);
return message;
}),
+ createTextEditorDecorationType: jest.fn(() => ({
+ dispose: jest.fn(),
+ })),
activeTextEditor: TextEditor,
visibleTextEditors: [],
};
diff --git a/test/ui/lineSelection.test.ts b/test/ui/lineSelection.test.ts
new file mode 100644
index 0000000..57c73ae
--- /dev/null
+++ b/test/ui/lineSelection.test.ts
@@ -0,0 +1,107 @@
+// test/line-selection-manager.test.ts
+import { LineSelectionManager } from '../../src/ui/lineSelectionManager';
+import { ContextManager } from '../../src/context/contextManager';
+import vscode from 'vscode';
+
+jest.mock('vscode');
+
+jest.mock('../../src/utils/hashDocs', () => ({
+ hashContent: jest.fn(() => 'mockHash'),
+}));
+
+describe('LineSelectionManager', () => {
+ let contextManagerMock: ContextManager;
+ let mockEditor: vscode.TextEditor;
+ let lineSelectionManager: LineSelectionManager;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ contextManagerMock = {
+ getWorkspaceData: jest.fn(() => ({
+ '/test/file.py': {
+ hash: 'mockHash',
+ smells: [
+ { symbol: 'PERF-001', occurences: [{ line: 5 }] },
+ { symbol: 'SEC-002', occurences: [{ line: 5 }] },
+ ],
+ },
+ })),
+ } as unknown as ContextManager;
+
+ mockEditor = {
+ document: {
+ fileName: '/test/file.py',
+ getText: jest.fn(() => 'mock content'),
+ lineAt: jest.fn((line) => ({ text: 'mock line content' })),
+ },
+ selection: {
+ start: { line: 4 }, // 0-based index, maps to line 5
+ isSingleLine: true,
+ } as any,
+ setDecorations: jest.fn(),
+ } as unknown as vscode.TextEditor;
+
+ lineSelectionManager = new LineSelectionManager(contextManagerMock);
+ });
+
+ it('should remove last comment if decoration exists', () => {
+ const disposeMock = jest.fn();
+ (lineSelectionManager as any).decoration = { dispose: disposeMock };
+
+ lineSelectionManager.removeLastComment();
+ expect(disposeMock).toHaveBeenCalled();
+ });
+
+ it('should not proceed if no editor is provided', () => {
+ expect(() => lineSelectionManager.commentLine(null as any)).not.toThrow();
+ });
+
+ it('should not add comment if no smells detected for file', () => {
+ (contextManagerMock.getWorkspaceData as jest.Mock).mockReturnValue({});
+ lineSelectionManager.commentLine(mockEditor);
+ expect(mockEditor.setDecorations).not.toHaveBeenCalled();
+ });
+
+ it('should not add comment if document hash does not match', () => {
+ (contextManagerMock.getWorkspaceData as jest.Mock).mockReturnValue({
+ '/test/file.py': { hash: 'differentHash', smells: [] },
+ });
+ lineSelectionManager.commentLine(mockEditor);
+ expect(mockEditor.setDecorations).not.toHaveBeenCalled();
+ });
+
+ it('should add a single-line comment if a smell is found', () => {
+ lineSelectionManager.commentLine(mockEditor);
+
+ expect(mockEditor.setDecorations).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.any(Array),
+ );
+ });
+
+ it('should display a combined comment if multiple smells exist', () => {
+ lineSelectionManager.commentLine(mockEditor);
+
+ const expectedComment = 'π Smell: PERF-001 | (+1)';
+ expect(mockEditor.setDecorations).toHaveBeenCalledWith(
+ expect.objectContaining({
+ after: expect.objectContaining({ contentText: expectedComment }),
+ }),
+ expect.any(Array),
+ );
+ });
+});
+
+// Mock updates for test/mocks/vscode-mock.ts
+export const mockVscode = {
+ window: {
+ createTextEditorDecorationType: jest.fn(() => ({ dispose: jest.fn() })),
+ },
+ Range: jest.fn((startLine, startChar, endLine, endChar) => ({
+ start: { line: startLine, character: startChar },
+ end: { line: endLine, character: endChar },
+ })),
+};
+
+jest.mock('vscode', () => mockVscode);
From 3400ebba27e01a30ce70a030f8f66f1bd5b512d7 Mon Sep 17 00:00:00 2001
From: Ayushi Amin
Date: Sat, 1 Mar 2025 22:06:34 -0500
Subject: [PATCH 60/83] Finished adding test cases for line selection manager
(#413)
---
test/mocks/vscode-mock.ts | 34 +++++++++++++--
test/ui/lineSelection.test.ts | 80 ++++++++++++++++++++++++++---------
2 files changed, 91 insertions(+), 23 deletions(-)
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
index b09cf3d..da6a1aa 100644
--- a/test/mocks/vscode-mock.ts
+++ b/test/mocks/vscode-mock.ts
@@ -30,6 +30,22 @@ export const TextEditor = {
setDecorations: jest.fn(),
};
+// Add Range mock class
+export class MockRange {
+ public start: { line: number; character: number };
+ public end: { line: number; character: number };
+
+ constructor(
+ startLine: number,
+ startCharacter: number,
+ endLine: number,
+ endCharacter: number,
+ ) {
+ this.start = { line: startLine, character: startCharacter };
+ this.end = { line: endLine, character: endCharacter };
+ }
+}
+
interface Window {
showInformationMessage: jest.Mock;
showErrorMessage: jest.Mock;
@@ -52,9 +68,17 @@ export const window = {
console.log('MOCK showWarningMessage:', message);
return message;
}),
- createTextEditorDecorationType: jest.fn(() => ({
- dispose: jest.fn(),
- })),
+ // Enhanced mock for `createTextEditorDecorationType`
+ createTextEditorDecorationType: jest.fn((decorationOptions) => {
+ console.log(
+ 'MOCK createTextEditorDecorationType called with:',
+ decorationOptions,
+ );
+ return {
+ dispose: jest.fn(),
+ decorationOptions, // Store the decoration options for testing
+ };
+ }),
activeTextEditor: TextEditor,
visibleTextEditors: [],
};
@@ -115,8 +139,8 @@ export class MockHover {
constructor(public contents: MockMarkdownString) {}
}
-// export const MarkdownString = MockMarkdownString;
export const Hover = MockHover;
+export const Range = MockRange; // Use MockRange here
export interface Vscode {
window: Window;
@@ -124,6 +148,7 @@ export interface Vscode {
languages: typeof languages;
commands: typeof commands;
Position: typeof Position;
+ Range: typeof Range; // Add Range to interface
Hover: typeof Hover;
}
@@ -133,6 +158,7 @@ const vscode: Vscode = {
languages,
commands,
Position,
+ Range, // Add Range mock
Hover,
};
diff --git a/test/ui/lineSelection.test.ts b/test/ui/lineSelection.test.ts
index 57c73ae..cd851d2 100644
--- a/test/ui/lineSelection.test.ts
+++ b/test/ui/lineSelection.test.ts
@@ -33,7 +33,7 @@ describe('LineSelectionManager', () => {
document: {
fileName: '/test/file.py',
getText: jest.fn(() => 'mock content'),
- lineAt: jest.fn((line) => ({ text: 'mock line content' })),
+ lineAt: jest.fn(() => ({ text: 'mock line content' })),
},
selection: {
start: { line: 4 }, // 0-based index, maps to line 5
@@ -71,6 +71,52 @@ describe('LineSelectionManager', () => {
expect(mockEditor.setDecorations).not.toHaveBeenCalled();
});
+ it('should not add comment for multi-line selections', () => {
+ // Set up multi-line selection
+ (mockEditor.selection as any).isSingleLine = false;
+
+ lineSelectionManager.commentLine(mockEditor);
+
+ expect(mockEditor.setDecorations).not.toHaveBeenCalled();
+ });
+
+ it('should not add comment when no smells exist at line', () => {
+ // Mock smells array with no matching line
+ (contextManagerMock.getWorkspaceData as jest.Mock).mockReturnValue({
+ '/test/file.py': {
+ hash: 'mockHash',
+ smells: [
+ { symbol: 'PERF-001', occurences: [{ line: 6 }] }, // Different line
+ { symbol: 'SEC-002', occurences: [{ line: 7 }] },
+ ],
+ },
+ });
+
+ lineSelectionManager.commentLine(mockEditor);
+
+ expect(mockEditor.setDecorations).not.toHaveBeenCalled();
+ });
+
+ it('should display single smell comment without count', () => {
+ // Mock single smell at line
+ (contextManagerMock.getWorkspaceData as jest.Mock).mockReturnValue({
+ '/test/file.py': {
+ hash: 'mockHash',
+ smells: [{ symbol: 'PERF-001', occurences: [{ line: 5 }] }],
+ },
+ });
+
+ lineSelectionManager.commentLine(mockEditor);
+
+ expect(vscode.window.createTextEditorDecorationType).toHaveBeenCalledWith(
+ expect.objectContaining({
+ after: expect.objectContaining({
+ contentText: 'π Smell: PERF-001',
+ }),
+ }),
+ );
+ });
+
it('should add a single-line comment if a smell is found', () => {
lineSelectionManager.commentLine(mockEditor);
@@ -83,25 +129,21 @@ describe('LineSelectionManager', () => {
it('should display a combined comment if multiple smells exist', () => {
lineSelectionManager.commentLine(mockEditor);
- const expectedComment = 'π Smell: PERF-001 | (+1)';
+ // Verify the decoration type was created with correct options
+ expect(vscode.window.createTextEditorDecorationType).toHaveBeenCalledWith({
+ isWholeLine: true,
+ after: {
+ contentText: expect.stringContaining('π Smell: PERF-001 | (+1)'),
+ color: 'rgb(153, 211, 212)',
+ margin: '0 0 0 10px',
+ textDecoration: 'none',
+ },
+ });
+
+ // Verify decorations were applied to correct range
expect(mockEditor.setDecorations).toHaveBeenCalledWith(
- expect.objectContaining({
- after: expect.objectContaining({ contentText: expectedComment }),
- }),
- expect.any(Array),
+ expect.any(Object), // The decoration type instance
+ [new vscode.Range(4, 0, 4, 0)], // Expected range
);
});
});
-
-// Mock updates for test/mocks/vscode-mock.ts
-export const mockVscode = {
- window: {
- createTextEditorDecorationType: jest.fn(() => ({ dispose: jest.fn() })),
- },
- Range: jest.fn((startLine, startChar, endLine, endChar) => ({
- start: { line: startLine, character: startChar },
- end: { line: endLine, character: endChar },
- })),
-};
-
-jest.mock('vscode', () => mockVscode);
From cf783586159a3fccafab05782939c60090904c7d Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 3 Mar 2025 10:36:37 -0500
Subject: [PATCH 61/83] Added unit tests for fileHighlighter module
fixes ssm-lab/capstone--source-code-optimizer#382
---
test/mocks/vscode-mock.ts | 44 +++++++++++++++-
test/ui/fileHighlighter.test.ts | 90 +++++++++++++++++++++++++++++++++
2 files changed, 133 insertions(+), 1 deletion(-)
create mode 100644 test/ui/fileHighlighter.test.ts
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
index 83bce84..33b32e9 100644
--- a/test/mocks/vscode-mock.ts
+++ b/test/mocks/vscode-mock.ts
@@ -18,11 +18,27 @@ export const TextEditor = {
getText: jest.fn(() => config.docText),
fileName: config.filePath,
languageId: 'python',
+ lineAt: jest.fn((line: number) => {
+ console.log('MOCK lineAt:', line);
+ return {
+ text: 'Mock line text',
+ };
+ }),
+ lineCount: 10,
},
selection: {
start: { line: 0, character: 0 },
end: { line: 0, character: 0 },
},
+ setDecorations: jest.fn(),
+};
+
+export interface TextEditorDecorationType {
+ dispose: jest.Mock;
+}
+
+const textEditorDecorationType: TextEditorDecorationType = {
+ dispose: jest.fn(),
};
interface Window {
@@ -31,9 +47,10 @@ interface Window {
showWarningMessage: jest.Mock;
activeTextEditor: any;
visibleTextEditors: any[];
+ createTextEditorDecorationType: jest.Mock;
}
-export const window = {
+export const window: Window = {
showInformationMessage: jest.fn(async (message: string) => {
console.log('MOCK showInformationMessage:', message);
return message;
@@ -48,6 +65,10 @@ export const window = {
}),
activeTextEditor: TextEditor,
visibleTextEditors: [],
+ createTextEditorDecorationType: jest.fn((_options: any) => {
+ console.log('MOCK createTextEditorDecorationType:');
+ return textEditorDecorationType;
+ }),
};
interface Workspace {
@@ -63,6 +84,19 @@ export const workspace: Workspace = {
})),
};
+export const OverviewRulerLane = {
+ Right: 'Right',
+};
+
+export const Range = class MockRange {
+ constructor(
+ public startLine: number,
+ public startCharacter: number,
+ public endLine: number,
+ public endCharacter: number,
+ ) {}
+};
+
// New mocks for hover functionality
export const languages = {
registerHoverProvider: jest.fn(() => ({
@@ -112,8 +146,12 @@ export const Hover = MockHover;
export interface Vscode {
window: Window;
workspace: Workspace;
+ TextEditor: typeof TextEditor;
+ TextEditorDecorationType: TextEditorDecorationType;
languages: typeof languages;
commands: typeof commands;
+ OverviewRulerLane: typeof OverviewRulerLane;
+ Range: typeof Range;
Position: typeof Position;
Hover: typeof Hover;
}
@@ -121,8 +159,12 @@ export interface Vscode {
const vscode: Vscode = {
window,
workspace,
+ TextEditor,
+ TextEditorDecorationType: textEditorDecorationType,
languages,
commands,
+ OverviewRulerLane,
+ Range,
Position,
Hover,
};
diff --git a/test/ui/fileHighlighter.test.ts b/test/ui/fileHighlighter.test.ts
new file mode 100644
index 0000000..32fceda
--- /dev/null
+++ b/test/ui/fileHighlighter.test.ts
@@ -0,0 +1,90 @@
+import { FileHighlighter } from '../../src/ui/fileHighlighter';
+import { ContextManager } from '../../src/context/contextManager';
+import vscode from '../mocks/vscode-mock';
+import { HoverManager } from '../../src/ui/hoverManager';
+import { MarkdownString } from 'vscode';
+
+jest.mock('vscode');
+
+describe('File Highlighter', () => {
+ let contextManagerMock: ContextManager;
+ let fileHighlighter: FileHighlighter;
+
+ beforeEach(() => {
+ // Reset all mocks before each test
+ jest.clearAllMocks();
+
+ // Mock ContextManager
+ contextManagerMock = {
+ getWorkspaceData: jest.fn(),
+ setWorkspaceData: jest.fn(),
+ } as unknown as ContextManager;
+
+ fileHighlighter = new FileHighlighter(contextManagerMock);
+ });
+
+ it('should create decorations', () => {
+ const color = 'red';
+ const decoration = fileHighlighter['getDecoration'](color);
+
+ // Assert decoration was created
+ expect(vscode.window.createTextEditorDecorationType).toHaveBeenCalled();
+ expect(decoration).toBeDefined();
+ });
+
+ it('should highlight smells', () => {
+ const smells = [
+ {
+ messageId: 'smell1',
+ occurences: [{ line: 1 }],
+ },
+ ] as unknown as Smell[];
+
+ jest.spyOn(HoverManager, 'getInstance').mockReturnValueOnce({
+ hoverContent: 'hover content' as unknown as MarkdownString,
+ } as unknown as HoverManager);
+
+ fileHighlighter.highlightSmell(vscode.window.activeTextEditor, smells, 'R1729');
+
+ // Assert decorations were set
+ expect(vscode.window.activeTextEditor.setDecorations).toHaveBeenCalled();
+ });
+
+ it('should not reset highlight decorations on first init', () => {
+ const smells = [
+ {
+ messageId: 'R1729',
+ occurences: [{ line: 1 }],
+ },
+ ] as unknown as Smell[];
+
+ jest.spyOn(HoverManager, 'getInstance').mockReturnValueOnce({
+ hoverContent: 'hover content' as unknown as MarkdownString,
+ } as unknown as HoverManager);
+
+ fileHighlighter.highlightSmells(vscode.window.activeTextEditor, smells);
+
+ // Assert decorations were set
+ expect(fileHighlighter['decorations'][0].dispose).not.toHaveBeenCalled();
+ });
+
+ it('should reset highlight decorations on subsequent calls', () => {
+ const smells = [
+ {
+ messageId: 'R1729',
+ occurences: [{ line: 1 }],
+ },
+ ] as unknown as Smell[];
+
+ jest.spyOn(HoverManager, 'getInstance').mockReturnValueOnce({
+ hoverContent: 'hover content' as unknown as MarkdownString,
+ } as unknown as HoverManager);
+
+ fileHighlighter.highlightSmells(vscode.window.activeTextEditor, smells);
+
+ fileHighlighter.highlightSmells(vscode.window.activeTextEditor, smells);
+
+ // Assert decorations were set
+ expect(fileHighlighter['decorations'][0].dispose).toHaveBeenCalled();
+ });
+});
From eba471158961751ab4cd235c16483e40ea87a8d0 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 3 Mar 2025 10:37:18 -0500
Subject: [PATCH 62/83] Added coverage reporting to jest config
---
.gitignore | 1 +
package-lock.json | 71 +++++++++++++++++++++++++++++++++++++++++++++++
package.json | 35 ++++++++++++++++++++++-
3 files changed, 106 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 0b60dfa..4a6b8bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ dist
node_modules
.vscode-test/
*.vsix
+coverage/
diff --git a/package-lock.json b/package-lock.json
index 4374e89..aee0627 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31,6 +31,7 @@
"eslint-plugin-unused-imports": "^4.1.4",
"husky": "^9.1.7",
"jest": "^29.7.0",
+ "jest-silent-reporter": "^0.6.0",
"lint-staged": "^15.4.3",
"prettier": "^3.5.2",
"prettier-plugin-tailwindcss": "^0.6.11",
@@ -4517,6 +4518,24 @@
"node": ">=8"
}
},
+ "node_modules/is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "dev": true,
+ "dependencies": {
+ "ci-info": "^2.0.0"
+ },
+ "bin": {
+ "is-ci": "bin.js"
+ }
+ },
+ "node_modules/is-ci/node_modules/ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@@ -5503,6 +5522,58 @@
"node": ">=8"
}
},
+ "node_modules/jest-silent-reporter": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/jest-silent-reporter/-/jest-silent-reporter-0.6.0.tgz",
+ "integrity": "sha512-4nmS+5o7ycVlvbQOTx7CnGdbBtP2646hnDgQpQLaVhjHcQNHD+gqBAetyjRDlgpZ8+8N82MWI59K+EX2LsVk7g==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "jest-util": "^26.0.0"
+ }
+ },
+ "node_modules/jest-silent-reporter/node_modules/@jest/types": {
+ "version": "26.6.2",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
+ "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^15.0.0",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 10.14.2"
+ }
+ },
+ "node_modules/jest-silent-reporter/node_modules/@types/yargs": {
+ "version": "15.0.19",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz",
+ "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==",
+ "dev": true,
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/jest-silent-reporter/node_modules/jest-util": {
+ "version": "26.6.2",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz",
+ "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^26.6.2",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.4",
+ "is-ci": "^2.0.0",
+ "micromatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">= 10.14.2"
+ }
+ },
"node_modules/jest-snapshot": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
diff --git a/package.json b/package.json
index f1d5df8..f67890b 100644
--- a/package.json
+++ b/package.json
@@ -182,7 +182,39 @@
],
"moduleNameMapper": {
"^vscode$": "/test/mocks/vscode-mock.ts"
- }
+ },
+ "reporters": [
+ [
+ "jest-silent-reporter",
+ {
+ "useDots": true
+ },
+ {
+ "showPaths": true
+ }
+ ]
+ ],
+ "collectCoverage": true,
+ "coverageReporters": [
+ "text",
+ "html",
+ "lcov"
+ ],
+ "coverageDirectory": "/coverage/",
+ "coverageThreshold": {
+ "global": {
+ "branches": 80,
+ "functions": 80,
+ "lines": 80,
+ "statements": 80
+ }
+ },
+ "collectCoverageFrom": [
+ "src/**/*.ts",
+ "!src/**/*.d.ts",
+ "!src/**/index.ts",
+ "!test/mocks/*"
+ ]
},
"lint-staged": {
"src/**/*.ts": [
@@ -206,6 +238,7 @@
"eslint-plugin-unused-imports": "^4.1.4",
"husky": "^9.1.7",
"jest": "^29.7.0",
+ "jest-silent-reporter": "^0.6.0",
"lint-staged": "^15.4.3",
"prettier": "^3.5.2",
"prettier-plugin-tailwindcss": "^0.6.11",
From 366fe719e7cf1d56d84b5a36783be5ae2b7fec1e Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 3 Mar 2025 12:46:34 -0500
Subject: [PATCH 63/83] Added unit tests for hashing module
fixes ssm-lab/captsone--source-code-optimizer#414
---
.prettierignore | 1 +
package.json | 19 ++++++-------
test/__mocks__/crypto.ts | 8 ++++++
test/mocks/vscode-mock.ts | 28 +++++++++++--------
test/utils/hashDocs.test.ts | 56 +++++++++++++++++++++++++++++++++++++
5 files changed, 89 insertions(+), 23 deletions(-)
create mode 100644 test/__mocks__/crypto.ts
create mode 100644 test/utils/hashDocs.test.ts
diff --git a/.prettierignore b/.prettierignore
index db2facf..77b3620 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -7,3 +7,4 @@ media
**/*.config.js
**/*.mjs'
**/*d.ts
+coverage/
diff --git a/package.json b/package.json
index f67890b..463aa3f 100644
--- a/package.json
+++ b/package.json
@@ -165,7 +165,7 @@
"scripts": {
"vscode:prepublish": "npm run package",
"compile": "webpack",
- "test": "jest",
+ "test": "jest --silent --verbose",
"test:watch": "jest --watchAll --no-cache",
"watch": "webpack --watch",
"package": "webpack --mode production --devtool hidden-source-map",
@@ -183,16 +183,13 @@
"moduleNameMapper": {
"^vscode$": "/test/mocks/vscode-mock.ts"
},
- "reporters": [
- [
- "jest-silent-reporter",
- {
- "useDots": true
- },
- {
- "showPaths": true
- }
- ]
+ "moduleDirectories": [
+ "node_modules",
+ "tests/__mocks__"
+ ],
+ "roots": [
+ "/src",
+ "/test"
],
"collectCoverage": true,
"coverageReporters": [
diff --git a/test/__mocks__/crypto.ts b/test/__mocks__/crypto.ts
new file mode 100644
index 0000000..9bd59a3
--- /dev/null
+++ b/test/__mocks__/crypto.ts
@@ -0,0 +1,8 @@
+// __mocks__/crypto.ts
+export default {
+ createHash: jest.fn(() => ({
+ update: jest.fn(() => ({
+ digest: jest.fn(() => 'mocked-hash'),
+ })),
+ })),
+};
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
index d150736..7716eb4 100644
--- a/test/mocks/vscode-mock.ts
+++ b/test/mocks/vscode-mock.ts
@@ -12,20 +12,22 @@ export const config: Config = {
docText: 'Mock document text',
};
+export const TextDocument = {
+ getText: jest.fn(() => config.docText),
+ fileName: config.filePath,
+ languageId: 'python',
+ lineAt: jest.fn((line: number) => {
+ console.log('MOCK lineAt:', line);
+ return {
+ text: 'Mock line text',
+ };
+ }),
+ lineCount: 10,
+};
+
// Mock for `vscode.TextEditor`
export const TextEditor = {
- document: {
- getText: jest.fn(() => config.docText),
- fileName: config.filePath,
- languageId: 'python',
- lineAt: jest.fn((line: number) => {
- console.log('MOCK lineAt:', line);
- return {
- text: 'Mock line text',
- };
- }),
- lineCount: 10,
- },
+ document: TextDocument,
selection: {
start: { line: 0, character: 0 },
end: { line: 0, character: 0 },
@@ -146,6 +148,7 @@ export const Hover = MockHover;
export interface Vscode {
window: Window;
workspace: Workspace;
+ TextDocument: typeof TextDocument;
TextEditor: typeof TextEditor;
TextEditorDecorationType: TextEditorDecorationType;
languages: typeof languages;
@@ -159,6 +162,7 @@ export interface Vscode {
const vscode: Vscode = {
window,
workspace,
+ TextDocument,
TextEditor,
TextEditorDecorationType: textEditorDecorationType,
languages,
diff --git a/test/utils/hashDocs.test.ts b/test/utils/hashDocs.test.ts
new file mode 100644
index 0000000..8f6aa27
--- /dev/null
+++ b/test/utils/hashDocs.test.ts
@@ -0,0 +1,56 @@
+import { ContextManager } from '../../src/context/contextManager';
+
+import { TextDocument } from '../mocks/vscode-mock';
+import { updateHash } from '../../src/utils/hashDocs';
+
+import crypto from 'crypto';
+
+jest.mock('crypto');
+
+describe('Hashing Tools', () => {
+ let contextManagerMock: ContextManager;
+
+ beforeEach(() => {
+ // Reset all mocks before each test
+ jest.clearAllMocks();
+
+ // Mock ContextManager
+ contextManagerMock = {
+ getWorkspaceData: jest.fn(),
+ setWorkspaceData: jest.fn(),
+ } as unknown as ContextManager;
+ });
+
+ it('should do nothing if the document hash has not changed', async () => {
+ jest.spyOn(contextManagerMock, 'getWorkspaceData').mockReturnValueOnce({
+ 'fake.py': 'mocked-hash',
+ });
+
+ await updateHash(contextManagerMock, TextDocument as any);
+
+ expect(crypto.createHash).toHaveBeenCalled();
+ expect(contextManagerMock.setWorkspaceData).not.toHaveBeenCalled();
+ });
+
+ it('should update the workspace storage if the doc hash changed', async () => {
+ jest.spyOn(contextManagerMock, 'getWorkspaceData').mockReturnValueOnce({
+ 'fake.py': 'someHash',
+ });
+
+ await updateHash(contextManagerMock, TextDocument as any);
+
+ expect(crypto.createHash).toHaveBeenCalled();
+ expect(contextManagerMock.setWorkspaceData).toHaveBeenCalled();
+ });
+
+ it('should update the workspace storage if no hash exists for the doc', async () => {
+ jest.spyOn(contextManagerMock, 'getWorkspaceData').mockReturnValueOnce({
+ 'otherFake.py': 'someHash',
+ });
+
+ await updateHash(contextManagerMock, TextDocument as any);
+
+ expect(crypto.createHash).toHaveBeenCalled();
+ expect(contextManagerMock.setWorkspaceData).toHaveBeenCalled();
+ });
+});
From 808cc9338b78d6c2b38fe5fc6e8ac785ba8a7af4 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Wed, 5 Mar 2025 15:59:59 -0500
Subject: [PATCH 64/83] Changed mention of "Energy Saved" to "Carbon Saved"
---
media/script.js | 4 ++--
media/webview.html | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/media/script.js b/media/script.js
index f1a782f..ae26b65 100644
--- a/media/script.js
+++ b/media/script.js
@@ -8,7 +8,7 @@ function updateWebView(data, sep) {
// Update Energy Saved
document.getElementById(
'energy'
- ).textContent = `Energy Saved: ${data.energySaved.toExponential(3)} kg CO2`;
+ ).textContent = `Carbon Saved: ${data.energySaved.toExponential(3)} kg CO2`;
// Populate Target File
const targetFile = data.targetFile;
@@ -64,7 +64,7 @@ function updateWebView(data, sep) {
// Function to clear the UI (for when refactoring is done)
function clearWebview() {
- document.getElementById('energy').textContent = 'Energy Saved: --';
+ document.getElementById('energy').textContent = 'Carbon Saved: --';
document.getElementById('target-file-list').innerHTML = '';
document.getElementById('affected-file-list').innerHTML = '';
diff --git a/media/webview.html b/media/webview.html
index 11278d4..4a1b2f6 100644
--- a/media/webview.html
+++ b/media/webview.html
@@ -14,7 +14,7 @@
Refactoring Summary
-
Energy Saved: --
+
Carbon Saved: --
Target File
Other Modified Files
From b9c34f6cecfc59dd95c1d28237b1afebc083f855 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri, 7 Mar 2025 16:55:15 -0500
Subject: [PATCH 65/83] fixed bug that caused race condition when trying to
apply a refactoring to a file
---
src/commands/refactorSmell.ts | 2 ++
src/ui/refactorView.ts | 1 -
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 22b6d89..0b66f0f 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -95,6 +95,8 @@ export async function refactorSelectedSmell(
return;
}
+ await vscode.workspace.save(editor.document.uri);
+
const refactorResult = await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
index 9b3faf5..ebd9709 100644
--- a/src/ui/refactorView.ts
+++ b/src/ui/refactorView.ts
@@ -188,7 +188,6 @@ export class RefactorSidebarProvider implements vscode.WebviewViewProvider {
for (const [original, refactored] of this._file_map.entries()) {
const content = await vscode.workspace.fs.readFile(refactored);
await vscode.workspace.fs.writeFile(original, content);
- await vscode.workspace.save(original);
console.log(`Applied refactoring to ${original.fsPath}`);
}
vscode.window.showInformationMessage('Refactoring applied successfully!');
From 2cd44b1999b1397017cbadcb2a93fa4647fb4d8a Mon Sep 17 00:00:00 2001
From: tbrar06
Date: Mon, 10 Mar 2025 12:13:28 -0400
Subject: [PATCH 66/83] ssm-lab/capstone--source-code-optimizer#445 Added unit
tests for api backend
---
test/api/backend.test.ts | 57 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 test/api/backend.test.ts
diff --git a/test/api/backend.test.ts b/test/api/backend.test.ts
new file mode 100644
index 0000000..8006806
--- /dev/null
+++ b/test/api/backend.test.ts
@@ -0,0 +1,57 @@
+import {
+ checkServerStatus,
+ initLogs,
+ fetchSmells,
+ refactorSmell,
+} from '../../src/api/backend';
+import { serverStatus } from '../../src/utils/serverStatus';
+import { ServerStatusType } from '../../src/utils/serverStatus';
+import * as vscode from '../mocks/vscode-mock';
+
+describe('backend', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('checkServerStatus should update serverStatus to UP on success', async () => {
+ global.fetch = jest.fn(() => Promise.resolve({ ok: true })) as jest.Mock;
+
+ const setStatusSpy = jest.spyOn(serverStatus, 'setStatus');
+
+ await checkServerStatus();
+
+ expect(setStatusSpy).toHaveBeenCalledWith(ServerStatusType.UP);
+ });
+
+ test('initLogs should return true on success', async () => {
+ global.fetch = jest.fn(() => Promise.resolve({ ok: true })) as jest.Mock;
+ const result = await initLogs('/path/to/logs');
+ expect(result).toBe(true);
+ });
+
+ test('fetchSmells should return smells array on success', async () => {
+ const mockSmells = [{ symbol: 'LongMethod', severity: 'HIGH' }];
+ global.fetch = jest.fn(() =>
+ Promise.resolve({ ok: true, json: () => Promise.resolve(mockSmells) }),
+ ) as jest.Mock;
+ const result = await fetchSmells('file.py', ['LongMethod']);
+ expect(result).toEqual(mockSmells);
+ });
+
+ test('refactorSmell should return refactor result on success', async () => {
+ const mockRefactorOutput = { success: true };
+
+ global.fetch = jest.fn(() =>
+ Promise.resolve({ ok: true, json: () => Promise.resolve(mockRefactorOutput) }),
+ ) as jest.Mock;
+
+ (vscode.workspace as any).workspaceFolders = [
+ { uri: { fsPath: '/mock/workspace' } },
+ ];
+
+ const result = await refactorSmell('/mock/workspace/file.py', {
+ symbol: 'LongMethod',
+ } as Smell);
+ expect(result).toEqual(mockRefactorOutput);
+ });
+});
From 0565e40d0dacf4ff9b7da85ca26f3d461c7376e0 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Mon, 10 Mar 2025 14:43:23 -0400
Subject: [PATCH 67/83] Added test for smells filter in settings
(https://github.com/ssm-lab/capstone--source-code-optimizer/issues/415)
---
test/utils/handleSmellSettings.test.ts | 111 +++++++++++++++++++++++++
1 file changed, 111 insertions(+)
create mode 100644 test/utils/handleSmellSettings.test.ts
diff --git a/test/utils/handleSmellSettings.test.ts b/test/utils/handleSmellSettings.test.ts
new file mode 100644
index 0000000..6ea155c
--- /dev/null
+++ b/test/utils/handleSmellSettings.test.ts
@@ -0,0 +1,111 @@
+import * as vscode from 'vscode';
+import {
+ handleSmellFilterUpdate,
+ getEnabledSmells,
+ formatSmellName,
+} from '../../src/utils/handleSmellSettings';
+import { wipeWorkCache } from '../../src/commands/wipeWorkCache';
+import { ContextManager } from '../../src/context/contextManager';
+import vscodeMock from '../mocks/vscode-mock';
+
+jest.mock('../../src/commands/wipeWorkCache', () => ({
+ wipeWorkCache: jest.fn(),
+}));
+
+describe('Settings Page - handleSmellSettings.ts', () => {
+ let contextManagerMock: ContextManager;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ contextManagerMock = {
+ getWorkspaceData: jest.fn(),
+ setWorkspaceData: jest.fn(),
+ } as unknown as ContextManager;
+ });
+
+ describe('getEnabledSmells', () => {
+ it('should return the current enabled smells from settings', () => {
+ const mockSmells = {
+ 'cached-repeated-calls': true,
+ 'long-element-chain': false,
+ };
+
+ jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
+ get: jest.fn().mockReturnValue(mockSmells),
+ } as any);
+
+ const enabledSmells = getEnabledSmells();
+
+ expect(enabledSmells).toEqual(mockSmells);
+ });
+
+ it('should return an empty object if no smells are set', () => {
+ jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
+ get: jest.fn().mockReturnValue({}),
+ } as any);
+
+ const enabledSmells = getEnabledSmells();
+ expect(enabledSmells).toEqual({});
+ });
+ });
+
+ describe('handleSmellFilterUpdate', () => {
+ it('should detect when a smell is enabled and notify the user', () => {
+ const previousSmells = { 'cached-repeated-calls': false };
+ const currentSmells = { 'cached-repeated-calls': true };
+
+ jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
+ get: jest.fn().mockReturnValue(currentSmells),
+ } as any);
+
+ handleSmellFilterUpdate(previousSmells, contextManagerMock);
+
+ expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
+ 'Eco: Enabled detection of Cached Repeated Calls.',
+ );
+ expect(wipeWorkCache).toHaveBeenCalledWith(contextManagerMock, 'settings');
+ });
+
+ it('should detect when a smell is disabled and notify the user', () => {
+ const previousSmells = { 'long-element-chain': true };
+ const currentSmells = { 'long-element-chain': false };
+
+ jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
+ get: jest.fn().mockReturnValue(currentSmells),
+ } as any);
+
+ handleSmellFilterUpdate(previousSmells, contextManagerMock);
+
+ expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
+ 'Eco: Disabled detection of Long Element Chain.',
+ );
+ expect(wipeWorkCache).toHaveBeenCalledWith(contextManagerMock, 'settings');
+ });
+
+ it('should not wipe cache if no smells changed', () => {
+ const previousSmells = { 'cached-repeated-calls': true };
+ const currentSmells = { 'cached-repeated-calls': true };
+
+ jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
+ get: jest.fn().mockReturnValue(currentSmells),
+ } as any);
+
+ handleSmellFilterUpdate(previousSmells, contextManagerMock);
+
+ expect(wipeWorkCache).not.toHaveBeenCalled();
+ expect(vscode.window.showInformationMessage).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('formatSmellName', () => {
+ it('should format kebab-case smell names to a readable format', () => {
+ expect(formatSmellName('cached-repeated-calls')).toBe('Cached Repeated Calls');
+ expect(formatSmellName('long-element-chain')).toBe('Long Element Chain');
+ expect(formatSmellName('string-concat-loop')).toBe('String Concat Loop');
+ });
+
+ it('should return an empty string if given an empty input', () => {
+ expect(formatSmellName('')).toBe('');
+ });
+ });
+});
From 8df531d1dce522fd2b02216e618192a77b8da071 Mon Sep 17 00:00:00 2001
From: tbrar06
Date: Mon, 10 Mar 2025 18:02:49 -0400
Subject: [PATCH 68/83] ssm-lab/capstone--source-code-optimizer#399 Added unit
tests for refactor smell command
---
test/commands/refactorSmell.test.ts | 297 ++++++++++++++++++++++++++++
1 file changed, 297 insertions(+)
create mode 100644 test/commands/refactorSmell.test.ts
diff --git a/test/commands/refactorSmell.test.ts b/test/commands/refactorSmell.test.ts
new file mode 100644
index 0000000..7702363
--- /dev/null
+++ b/test/commands/refactorSmell.test.ts
@@ -0,0 +1,297 @@
+import * as vscode from 'vscode';
+import { refactorSelectedSmell } from '../../src/commands/refactorSmell';
+import { ContextManager } from '../../src/context/contextManager';
+import { refactorSmell } from '../../src/api/backend';
+import { FileHighlighter } from '../../src/ui/fileHighlighter';
+import { envConfig } from '../../src/utils/envConfig';
+import { Smell } from '../../src/types';
+
+// mock VSCode APIs
+jest.mock('vscode', () => ({
+ window: {
+ showErrorMessage: jest.fn(),
+ showWarningMessage: jest.fn(),
+ showInformationMessage: jest.fn(),
+ withProgress: jest.fn((options, task) => task()),
+ activeTextEditor: undefined,
+ showTextDocument: jest.fn().mockResolvedValue(undefined),
+ },
+ workspace: {
+ save: jest.fn(),
+ getConfiguration: jest.fn(),
+ openTextDocument: jest.fn().mockImplementation(async (uri) => ({
+ // Mock TextDocument object
+ uri: typeof uri === 'string' ? { fsPath: uri } : uri,
+ fileName: typeof uri === 'string' ? uri : uri.fsPath,
+ getText: jest.fn().mockReturnValue('mock content'),
+ })),
+ },
+ ProgressLocation: {
+ Notification: 1,
+ },
+ Uri: {
+ file: jest.fn((path) => ({
+ toString: () => `file://${path}`,
+ fsPath: path,
+ })),
+ },
+ commands: {
+ executeCommand: jest.fn(),
+ },
+ ViewColumn: {
+ Beside: 2,
+ },
+}));
+
+// mock backend API
+jest.mock('../../src/api/backend', () => ({
+ refactorSmell: jest.fn(),
+}));
+
+// mock FileHighlighter
+jest.mock('../../src/ui/fileHighlighter', () => ({
+ FileHighlighter: jest.fn().mockImplementation(() => ({
+ highlightSmells: jest.fn(),
+ })),
+}));
+
+// mock setTimeout
+jest.mock('timers/promises', () => ({
+ setTimeout: jest.fn().mockResolvedValue(undefined),
+}));
+
+describe('refactorSelectedSmell', () => {
+ let mockContextManager: jest.Mocked;
+ let mockEditor: any;
+ let mockDocument: any;
+ let mockSelection: any;
+
+ const createMockSmell = (line: number): Smell => ({
+ messageId: 'R0913',
+ type: 'refactor',
+ message: 'Too many arguments (8/6)',
+ confidence: 'HIGH',
+ path: 'fake.py',
+ symbol: 'too-many-arguments',
+ module: 'test-module',
+ occurences: [
+ {
+ line,
+ column: 1,
+ },
+ ],
+ additionalInfo: {},
+ });
+
+ beforeEach(() => {
+ // reset all mocks
+ jest.clearAllMocks();
+
+ // setup mock context manager
+ mockContextManager = {
+ getWorkspaceData: jest.fn(),
+ setWorkspaceData: jest.fn(),
+ } as any;
+
+ // setup mock selection
+ mockSelection = {
+ start: { line: 0 }, // Line 1 in VS Code's 0-based indexing
+ end: { line: 0 },
+ };
+
+ // setup mock document
+ mockDocument = {
+ getText: jest.fn().mockReturnValue('mock content'),
+ uri: { fsPath: '/test/file.ts' },
+ };
+
+ // setup mock editor
+ mockEditor = {
+ document: mockDocument,
+ selection: mockSelection,
+ };
+
+ // reset vscode.window.activeTextEditor
+ (vscode.window as any).activeTextEditor = mockEditor;
+
+ // reset commands mock
+ (vscode.commands.executeCommand as jest.Mock).mockResolvedValue(undefined);
+ });
+
+ test('should show error when no active editor', async () => {
+ (vscode.window as any).activeTextEditor = undefined;
+
+ await refactorSelectedSmell(mockContextManager);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: Unable to proceed as no active editor or file path found.',
+ );
+ });
+
+ test('should show error when no smells detected', async () => {
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: [],
+ },
+ };
+ }
+ return null;
+ });
+
+ await refactorSelectedSmell(mockContextManager);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: No smells detected in the file for refactoring.',
+ );
+ });
+
+ test('should show error when no matching smell found for selected line', async () => {
+ const mockSmells = [createMockSmell(5)];
+
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: mockSmells,
+ },
+ };
+ }
+ return null;
+ });
+
+ await refactorSelectedSmell(mockContextManager);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: No matching smell found for refactoring.',
+ );
+ });
+
+ test('should successfully refactor a smell when found', async () => {
+ const mockSmells = [createMockSmell(1)];
+
+ const mockRefactorResult = {
+ refactoredData: {
+ tempDir: '/tmp/test',
+ targetFile: {
+ original: '/test/file.ts',
+ refactored: '/test/file.refactored.ts',
+ },
+ affectedFiles: [
+ {
+ original: '/test/other.ts',
+ refactored: '/test/other.refactored.ts',
+ },
+ ],
+ energySaved: 10,
+ },
+ updatedSmells: [
+ {
+ ...createMockSmell(1),
+ messageId: 'updated-smell',
+ symbol: 'UpdatedSmell',
+ message: 'Updated message',
+ },
+ ],
+ };
+
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: mockSmells,
+ },
+ };
+ }
+ return null;
+ });
+
+ (refactorSmell as jest.Mock).mockResolvedValue(mockRefactorResult);
+
+ await refactorSelectedSmell(mockContextManager);
+
+ expect(vscode.workspace.save).toHaveBeenCalled();
+ expect(refactorSmell).toHaveBeenCalledWith('/test/file.ts', mockSmells[0]);
+ expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
+ 'Refactoring report available in sidebar.',
+ );
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
+ 'extension.refactorSidebar.focus',
+ );
+ expect(vscode.workspace.openTextDocument).toHaveBeenCalled();
+ expect(vscode.window.showTextDocument).toHaveBeenCalled();
+ expect(FileHighlighter).toHaveBeenCalled();
+ });
+
+ test('should handle refactoring failure', async () => {
+ const mockSmells = [createMockSmell(1)];
+
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: mockSmells,
+ },
+ };
+ }
+ return null;
+ });
+
+ (refactorSmell as jest.Mock).mockRejectedValue(new Error('Refactoring failed'));
+
+ await refactorSelectedSmell(mockContextManager);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: Refactoring failed. See console for details.',
+ );
+ });
+
+ test('should handle given smell parameter', async () => {
+ const givenSmell = createMockSmell(3);
+ const mockSmells = [givenSmell];
+
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: mockSmells,
+ },
+ };
+ }
+ return null;
+ });
+
+ const mockRefactorResult = {
+ refactoredData: {
+ tempDir: '/tmp/test',
+ targetFile: {
+ original: '/test/file.ts',
+ refactored: '/test/file.refactored.ts',
+ },
+ affectedFiles: [
+ {
+ original: '/test/other.ts',
+ refactored: '/test/other.refactored.ts',
+ },
+ ],
+ energySaved: 10,
+ },
+ updatedSmells: [],
+ };
+
+ (refactorSmell as jest.Mock).mockResolvedValue(mockRefactorResult);
+
+ await refactorSelectedSmell(mockContextManager, givenSmell);
+
+ expect(refactorSmell).toHaveBeenCalledWith('/test/file.ts', givenSmell);
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
+ 'extension.refactorSidebar.focus',
+ );
+ expect(vscode.workspace.openTextDocument).toHaveBeenCalled();
+ expect(vscode.window.showTextDocument).toHaveBeenCalled();
+ expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(
+ 'Eco: No updated smells detected after refactoring.',
+ );
+ });
+});
From 5b8b5e085717613ae58cecec0a97fec2684b2131 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri, 14 Mar 2025 21:50:46 -0400
Subject: [PATCH 69/83] update webpack config
---
webpack.config.js | 53 +++++++++++++++++------------------------------
1 file changed, 19 insertions(+), 34 deletions(-)
diff --git a/webpack.config.js b/webpack.config.js
index 56b3e0a..ea0eca6 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,54 +1,39 @@
-//@ts-check
-
-'use strict';
-
const path = require('path');
+const nodeExternals = require('webpack-node-externals');
const Dotenv = require('dotenv-webpack');
-//@ts-check
-/** @typedef {import('webpack').Configuration} WebpackConfig **/
-
-/** @type WebpackConfig */
-const extensionConfig = {
- target: 'node', // VS Code extensions run in a Node.js-context π -> https://webpack.js.org/configuration/node/
- mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
-
- entry: './src/extension.ts', // the entry point of this extension, π -> https://webpack.js.org/configuration/entry-context/
+module.exports = {
+ target: 'node',
+ entry: './src/extension.ts',
output: {
- // the bundle is stored in the 'dist' folder (check package.json), π -> https://webpack.js.org/configuration/output/
path: path.resolve(__dirname, 'dist'),
filename: 'extension.js',
- libraryTarget: 'commonjs2'
- },
- externals: {
- vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, π -> https://webpack.js.org/configuration/externals/
- // modules added here also need to be added in the .vscodeignore file
+ libraryTarget: 'commonjs2',
},
resolve: {
- // support reading TypeScript and JavaScript files, π -> https://github.com/TypeStrong/ts-loader
- extensions: ['.ts', '.js']
+ extensions: ['.ts', '.js'],
},
+ externals: [
+ nodeExternals(),
+ { vscode: 'commonjs vscode' },
+ ],
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
- use: [
- {
- loader: 'ts-loader'
- }
- ]
- }
- ]
+ use: 'ts-loader',
+ },
+ ],
},
- devtool: 'nosources-source-map',
+ mode: 'development',
+ devtool: 'source-map',
infrastructureLogging: {
level: 'log' // enables logging required for problem matchers
},
plugins: [
new Dotenv({
- path: './.env' // Path to your .env file
- })
- ]
-};
-module.exports = [extensionConfig];
+ path: './.env',
+ }),
+ ],
+};
\ No newline at end of file
From 11297fa4a6da5a1dffb909af7e30511fb6c420ab Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri, 14 Mar 2025 22:25:35 -0400
Subject: [PATCH 70/83] Added jest testing workflow
Squashed commit of the following:
commit e270c5ec6514827732748c744b52c903265729fb
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri Mar 14 22:20:38 2025 -0400
Updated jest coverage specifications
commit d068105fcd8f6d83ebe6fd6cb02adba7ff10f703
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri Mar 14 22:05:24 2025 -0400
Updated worflow to re-run on pr update
commit f96dffa188044d46fb29d2736e68f2971f0f0702
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri Mar 14 22:03:44 2025 -0400
Modified workflow to upload coverage report even on failure
commit a662f0e90f6d85b8452fed1582c42d34362ffead
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri Mar 14 22:00:00 2025 -0400
Added jest testing workflow
---
.github/workflows/jest-tests.yaml | 32 +++++++++++++++++++++++++++++++
package.json | 8 ++++----
2 files changed, 36 insertions(+), 4 deletions(-)
create mode 100644 .github/workflows/jest-tests.yaml
diff --git a/.github/workflows/jest-tests.yaml b/.github/workflows/jest-tests.yaml
new file mode 100644
index 0000000..60c8bc3
--- /dev/null
+++ b/.github/workflows/jest-tests.yaml
@@ -0,0 +1,32 @@
+name: Jest Tests and Coverage Check
+
+on:
+ pull_request:
+ types: [opened, reopened, synchronize]
+ branches: [dev]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: Run Jest tests
+ run: npm test -- --coverage
+
+ - name: Upload coverage report
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-report
+ path: coverage/
diff --git a/package.json b/package.json
index 463aa3f..abe3f28 100644
--- a/package.json
+++ b/package.json
@@ -200,9 +200,6 @@
"coverageDirectory": "/coverage/",
"coverageThreshold": {
"global": {
- "branches": 80,
- "functions": 80,
- "lines": 80,
"statements": 80
}
},
@@ -210,7 +207,10 @@
"src/**/*.ts",
"!src/**/*.d.ts",
"!src/**/index.ts",
- "!test/mocks/*"
+ "!test/mocks/*",
+ "!src/extension.ts",
+ "!src/context/*",
+ "!src/utils/configManager.ts"
]
},
"lint-staged": {
From af9b36196e3236c9edead9434221b51e4f4a297b Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 15 Mar 2025 13:35:59 -0400
Subject: [PATCH 71/83] Added test cases for backend
---
package.json | 2 +-
src/api/backend.ts | 18 ++--
test/api/backend.test.ts | 206 +++++++++++++++++++++++++++++++++------
3 files changed, 189 insertions(+), 37 deletions(-)
diff --git a/package.json b/package.json
index abe3f28..345f538 100644
--- a/package.json
+++ b/package.json
@@ -166,7 +166,7 @@
"vscode:prepublish": "npm run package",
"compile": "webpack",
"test": "jest --silent --verbose",
- "test:watch": "jest --watchAll --no-cache",
+ "test:watch": "jest --watch --silent --verbose",
"watch": "webpack --watch",
"package": "webpack --mode production --devtool hidden-source-map",
"compile-tests": "tsc -p . --outDir out",
diff --git a/src/api/backend.ts b/src/api/backend.ts
index beb3af6..d5979ea 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -74,7 +74,7 @@ export async function fetchSmells(
`Eco: API request failed (${response.status} - ${response.statusText})`,
);
vscode.window.showErrorMessage(
- `Eco: Failed to fetch smells (HTTP ${response.status})`,
+ `Eco: Failed to fetch smells`,
);
return [];
}
@@ -83,7 +83,7 @@ export async function fetchSmells(
if (!Array.isArray(smellsList)) {
console.error('Eco: Invalid response format from backend.');
- vscode.window.showErrorMessage('Eco: Unexpected response from backend.');
+ vscode.window.showErrorMessage('Eco: Failed to fetch smells');
return [];
}
@@ -92,7 +92,7 @@ export async function fetchSmells(
} catch (error: any) {
console.error(`Eco: Network error while fetching smells: ${error.message}`);
vscode.window.showErrorMessage(
- 'Eco: Unable to reach the backend. Please check your connection.',
+ 'Eco: Failed to fetch smells',
);
return [];
}
@@ -105,23 +105,25 @@ export async function refactorSmell(
): Promise {
const url = `${BASE_URL}/refactor`;
- const workspace_folder = vscode.workspace.workspaceFolders?.find((folder) =>
+ const workspaceFolder = vscode.workspace.workspaceFolders?.find((folder) =>
filePath.includes(folder.uri.fsPath),
- )?.uri.fsPath;
+ )
- if (!workspace_folder) {
+ if (!workspaceFolder) {
console.error('Eco: Error - Unable to determine workspace folder for', filePath);
throw new Error(
`Eco: Unable to find a matching workspace folder for file: ${filePath}`,
);
}
+ const workspaceFolderPath = workspaceFolder.uri.fsPath;
+
console.log(
- `Eco: Initiating refactoring for smell "${smell.symbol}" in "${workspace_folder}"`,
+ `Eco: Initiating refactoring for smell "${smell.symbol}" in "${workspaceFolderPath}"`,
);
const payload = {
- source_dir: workspace_folder,
+ source_dir: workspaceFolderPath,
smell,
};
diff --git a/test/api/backend.test.ts b/test/api/backend.test.ts
index 8006806..2b7428f 100644
--- a/test/api/backend.test.ts
+++ b/test/api/backend.test.ts
@@ -13,45 +13,195 @@ describe('backend', () => {
jest.clearAllMocks();
});
- test('checkServerStatus should update serverStatus to UP on success', async () => {
- global.fetch = jest.fn(() => Promise.resolve({ ok: true })) as jest.Mock;
+ describe('checkServerStatus', () => {
+ test('checkServerStatus should update serverStatus to UP on success', async () => {
+ global.fetch = jest.fn(() => Promise.resolve({ ok: true })) as jest.Mock;
- const setStatusSpy = jest.spyOn(serverStatus, 'setStatus');
+ const setStatusSpy = jest.spyOn(serverStatus, 'setStatus');
- await checkServerStatus();
+ await checkServerStatus();
- expect(setStatusSpy).toHaveBeenCalledWith(ServerStatusType.UP);
+ expect(setStatusSpy).toHaveBeenCalledWith(ServerStatusType.UP);
+ });
+
+ test('checkServerStatus should update serverStatus to DOWN on non-success', async () => {
+ global.fetch = jest.fn(() => Promise.resolve({ ok: false })) as jest.Mock;
+
+ const setStatusSpy = jest.spyOn(serverStatus, 'setStatus');
+
+ await checkServerStatus();
+
+ expect(setStatusSpy).toHaveBeenCalledWith(ServerStatusType.DOWN);
+ });
+
+ test('checkServerStatus should update serverStatus to DOWN on error', async () => {
+ global.fetch = jest.fn(() =>
+ Promise.reject("Can't connect to server"),
+ ) as jest.Mock;
+
+ const setStatusSpy = jest.spyOn(serverStatus, 'setStatus');
+
+ await checkServerStatus();
+
+ expect(setStatusSpy).toHaveBeenCalledWith(ServerStatusType.DOWN);
+ });
});
- test('initLogs should return true on success', async () => {
- global.fetch = jest.fn(() => Promise.resolve({ ok: true })) as jest.Mock;
- const result = await initLogs('/path/to/logs');
- expect(result).toBe(true);
+ describe('initLogs', () => {
+ test('initLogs should return true on success', async () => {
+ global.fetch = jest.fn(() => Promise.resolve({ ok: true })) as jest.Mock;
+ const result = await initLogs('/path/to/logs');
+ expect(result).toBe(true);
+ });
+
+ test('initLogs should return false on non success', async () => {
+ global.fetch = jest.fn(() => Promise.resolve({ ok: false })) as jest.Mock;
+ const result = await initLogs('/path/to/logs');
+ expect(result).toBe(false);
+ });
+
+ test('initLogs should return false on error', async () => {
+ global.fetch = jest.fn(() => {
+ throw new Error('Some error');
+ }) as jest.Mock;
+ const result = await initLogs('/path/to/logs');
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: Unable to reach the backend. Please check your connection.',
+ );
+
+ expect(result).toBe(false);
+ });
});
- test('fetchSmells should return smells array on success', async () => {
- const mockSmells = [{ symbol: 'LongMethod', severity: 'HIGH' }];
- global.fetch = jest.fn(() =>
- Promise.resolve({ ok: true, json: () => Promise.resolve(mockSmells) }),
- ) as jest.Mock;
- const result = await fetchSmells('file.py', ['LongMethod']);
- expect(result).toEqual(mockSmells);
+ describe('fetchSmells', () => {
+ test('fetchSmells should return smells array on success', async () => {
+ const mockSmells = [{ symbol: 'LongMethod', severity: 'HIGH' }];
+ global.fetch = jest.fn(() =>
+ Promise.resolve({ ok: true, json: () => Promise.resolve(mockSmells) }),
+ ) as jest.Mock;
+
+ const result = await fetchSmells('file.py', ['LongMethod']);
+ expect(result).toEqual(mockSmells);
+ });
+
+ test('fetchSmells should return an empty array on status not ok', async () => {
+ global.fetch = jest.fn(() =>
+ Promise.resolve({ ok: false, status: 400, json: () => Promise.resolve([]) }),
+ ) as jest.Mock;
+
+ const result = await fetchSmells('file.py', ['LongMethod']);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ `Eco: Failed to fetch smells`,
+ );
+ expect(result).toEqual([]);
+ });
+
+ test('fetchSmells should return an empty array on invalid response format', async () => {
+ global.fetch = jest.fn(() =>
+ Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(200) }),
+ ) as jest.Mock;
+
+ const result = await fetchSmells('file.py', ['LongMethod']);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ `Eco: Failed to fetch smells`,
+ );
+ expect(result).toEqual([]);
+ });
+
+ test('fetchSmells should return an empty array on error', async () => {
+ global.fetch = jest.fn(() => {
+ throw new Error('Some error');
+ }) as jest.Mock;
+
+ const result = await fetchSmells('file.py', ['LongMethod']);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ `Eco: Failed to fetch smells`,
+ );
+ expect(result).toEqual([]);
+ });
});
- test('refactorSmell should return refactor result on success', async () => {
- const mockRefactorOutput = { success: true };
+ describe('refactorSmell', () => {
+ test('refactorSmell should return refactor result on success', async () => {
+ const mockRefactorOutput = { success: true };
+
+ global.fetch = jest.fn(() =>
+ Promise.resolve({
+ ok: true,
+ json: () => Promise.resolve(mockRefactorOutput),
+ }),
+ ) as jest.Mock;
+
+ (vscode.workspace as any).workspaceFolders = [
+ { uri: { fsPath: '/mock/workspace' } },
+ ];
+
+ const result = await refactorSmell('/mock/workspace/file.py', {
+ symbol: 'LongMethod',
+ } as Smell);
+ expect(result).toEqual(mockRefactorOutput);
+ });
+
+ test('refactorSmell should throw and error if no workspace found', async () => {
+ const mockRefactorOutput = { success: true };
+
+ global.fetch = jest.fn(() =>
+ Promise.resolve({
+ ok: true,
+ json: () => Promise.resolve(mockRefactorOutput),
+ }),
+ ) as jest.Mock;
+
+ (vscode.workspace as any).workspaceFolders = [
+ { uri: { fsPath: '/mock/workspace' } },
+ ];
+
+ await expect(
+ refactorSmell('/mock/another-workspace/file.py', {
+ symbol: 'LongMethod',
+ } as Smell),
+ ).rejects.toThrow(
+ 'Eco: Unable to find a matching workspace folder for file: /mock/another-workspace/file.py',
+ );
+ });
+
+ test('refactorSmell should throw and error if not ok response', async () => {
+ global.fetch = jest.fn(() =>
+ Promise.resolve({
+ ok: false,
+ text: jest.fn().mockReturnValue('Some error text'),
+ }),
+ ) as jest.Mock;
+
+ (vscode.workspace as any).workspaceFolders = [
+ { uri: { fsPath: '/mock/workspace' } },
+ ];
+
+ await expect(
+ refactorSmell('/mock/workspace/file.py', {
+ symbol: 'LongMethod',
+ } as Smell),
+ ).rejects.toThrow('Some error text');
+ });
- global.fetch = jest.fn(() =>
- Promise.resolve({ ok: true, json: () => Promise.resolve(mockRefactorOutput) }),
- ) as jest.Mock;
+ test('refactorSmell should throw and error if function returns an error', async () => {
+ global.fetch = jest.fn(() => {
+ throw new Error('Some error');
+ }) as jest.Mock;
- (vscode.workspace as any).workspaceFolders = [
- { uri: { fsPath: '/mock/workspace' } },
- ];
+ (vscode.workspace as any).workspaceFolders = [
+ { uri: { fsPath: '/mock/workspace' } },
+ ];
- const result = await refactorSmell('/mock/workspace/file.py', {
- symbol: 'LongMethod',
- } as Smell);
- expect(result).toEqual(mockRefactorOutput);
+ await expect(
+ refactorSmell('/mock/workspace/file.py', {
+ symbol: 'LongMethod',
+ } as Smell),
+ ).rejects.toThrow('Some error');
+ });
});
});
From c684c1600026b2cafa7a7b22f0012f42052cc65f Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 15 Mar 2025 15:39:11 -0400
Subject: [PATCH 72/83] Added test cases for refactorSmell command module
---
package.json | 2 +-
src/commands/refactorSmell.ts | 244 ++++++++++----------
test/commands/refactorSmell.test.ts | 332 +++++++++++++++-------------
3 files changed, 295 insertions(+), 283 deletions(-)
diff --git a/package.json b/package.json
index 345f538..9a1d884 100644
--- a/package.json
+++ b/package.json
@@ -165,7 +165,7 @@
"scripts": {
"vscode:prepublish": "npm run package",
"compile": "webpack",
- "test": "jest --silent --verbose",
+ "test": "jest --verbose",
"test:watch": "jest --watch --silent --verbose",
"watch": "webpack --watch",
"package": "webpack --mode production --devtool hidden-source-map",
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 0b66f0f..cd1b776 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -13,6 +13,7 @@ import { setTimeout } from 'timers/promises';
import { serverStatus } from '../utils/serverStatus';
import { ServerStatusType } from '../utils/serverStatus';
+/* istanbul ignore next */
serverStatus.on('change', (newStatus: ServerStatusType) => {
console.log('Server status changed:', newStatus);
if (newStatus === ServerStatusType.DOWN) {
@@ -53,6 +54,7 @@ export async function refactorSelectedSmell(
// Clean up temp directory if not removed
if (pastData) {
+ console.log('cleaning up temps');
cleanTemps(pastData);
}
@@ -137,141 +139,123 @@ export async function refactorSelectedSmell(
}
export async function refactorAllSmellsOfType(
+ // eslint-disable-next-line unused-imports/no-unused-vars
contextManager: ContextManager,
+ // eslint-disable-next-line unused-imports/no-unused-vars
smellId: string,
): Promise {
- const { editor, filePath } = getEditorAndFilePath();
-
- const pastData = contextManager.getWorkspaceData(
- envConfig.CURRENT_REFACTOR_DATA_KEY!,
- );
-
- // Clean up temp directory if not removed
- if (pastData) {
- cleanTemps(pastData);
- }
-
- if (!editor) {
- vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as no active editor found.',
- );
- console.log('No active editor found to refactor smell. Returning back.');
- return;
- }
- if (!filePath) {
- vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as active editor does not have a valid file path.',
- );
- console.log('No valid file path found to refactor smell. Returning back.');
- return;
- }
-
- // only account for one selection to be refactored for now
- // const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
-
- const smellsData: Smell[] = contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!,
- )[filePath].smells;
-
- if (!smellsData || smellsData.length === 0) {
- vscode.window.showErrorMessage(
- 'Eco: No smells detected in the file for refactoring.',
- );
- console.log('No smells found in the file for refactoring.');
- return;
- }
-
- // Filter smells by the given type ID
- const smellsOfType = smellsData.filter(
- (smell: Smell) => smell.messageId === smellId,
- );
-
- if (smellsOfType.length === 0) {
- vscode.window.showWarningMessage(
- `Eco: No smells of type ${smellId} found in the file.`,
- );
- return;
- }
-
- let combinedRefactoredData = '';
- let totalEnergySaved = 0;
- let allUpdatedSmells: Smell[] = [];
-
- // Refactor each smell of the given type
- for (const smell of smellsOfType) {
- const refactorResult = await refactorLine(smell, filePath);
-
- if (refactorResult && refactorResult.refactoredData) {
- // Add two newlines between each refactored result
- if (combinedRefactoredData) {
- combinedRefactoredData += '\n\n';
- }
-
- fs.readFile(
- refactorResult.refactoredData.targetFile.refactored,
- (err, data) => {
- if (!err) {
- combinedRefactoredData += data.toString('utf8');
- }
- },
- );
-
- totalEnergySaved += refactorResult.refactoredData.energySaved;
-
- if (refactorResult.updatedSmells) {
- allUpdatedSmells = [...allUpdatedSmells, ...refactorResult.updatedSmells];
- }
- }
- }
-
- /*
- Once all refactorings are merge, need to write to a file so that it has a path that
- will be the new `targetFile`. Also need to reconstruct the `RefactoredData` object
- by combining all `affectedFiles` merge to new paths if applicable. Once implemented,
- just uncomment lines below and pass in the refactoredData.
- */
-
- // Tentative data structure to be built below, change inputs as needed but needs
- // to implement the `MultiRefactoredData` interface
-
- // For any temp files that need to be written due to merging, I'd suggest writing them all
- // to one temp directory and add that directory to allTempDirs, that way they will be removed
-
- // UNCOMMENT ME WHEN READY
- // const combinedRefactoredData: MultiRefactoredData = {
- // targetFile: combinedTargetFile,
- // affectedFiles: allAffectedFiles,
- // energySaved: totalEnergySaved,
- // tempDirs: allTempDirs
+ // const { editor, filePath } = getEditorAndFilePath();
+ // const pastData = contextManager.getWorkspaceData(
+ // envConfig.CURRENT_REFACTOR_DATA_KEY!,
+ // );
+ // // Clean up temp directory if not removed
+ // if (pastData) {
+ // cleanTemps(pastData);
+ // }
+ // if (!editor) {
+ // vscode.window.showErrorMessage(
+ // 'Eco: Unable to proceed as no active editor found.',
+ // );
+ // console.log('No active editor found to refactor smell. Returning back.');
+ // return;
+ // }
+ // if (!filePath) {
+ // vscode.window.showErrorMessage(
+ // 'Eco: Unable to proceed as active editor does not have a valid file path.',
+ // );
+ // console.log('No valid file path found to refactor smell. Returning back.');
+ // return;
+ // }
+ // // only account for one selection to be refactored for now
+ // // const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
+ // const smellsData: Smell[] = contextManager.getWorkspaceData(
+ // envConfig.SMELL_MAP_KEY!,
+ // )[filePath].smells;
+ // if (!smellsData || smellsData.length === 0) {
+ // vscode.window.showErrorMessage(
+ // 'Eco: No smells detected in the file for refactoring.',
+ // );
+ // console.log('No smells found in the file for refactoring.');
+ // return;
+ // }
+ // // Filter smells by the given type ID
+ // const smellsOfType = smellsData.filter(
+ // (smell: Smell) => smell.messageId === smellId,
+ // );
+ // if (smellsOfType.length === 0) {
+ // vscode.window.showWarningMessage(
+ // `Eco: No smells of type ${smellId} found in the file.`,
+ // );
+ // return;
+ // }
+ // let combinedRefactoredData = '';
+ // let totalEnergySaved = 0;
+ // let allUpdatedSmells: Smell[] = [];
+ // // Refactor each smell of the given type
+ // for (const smell of smellsOfType) {
+ // const refactorResult = await refactorLine(smell, filePath);
+ // if (refactorResult && refactorResult.refactoredData) {
+ // // Add two newlines between each refactored result
+ // if (combinedRefactoredData) {
+ // combinedRefactoredData += '\n\n';
+ // }
+ // fs.readFile(
+ // refactorResult.refactoredData.targetFile.refactored,
+ // (err, data) => {
+ // if (!err) {
+ // combinedRefactoredData += data.toString('utf8');
+ // }
+ // },
+ // );
+ // totalEnergySaved += refactorResult.refactoredData.energySaved;
+ // if (refactorResult.updatedSmells) {
+ // allUpdatedSmells = [...allUpdatedSmells, ...refactorResult.updatedSmells];
+ // }
+ // }
+ // }
+ // /*
+ // Once all refactorings are merge, need to write to a file so that it has a path that
+ // will be the new `targetFile`. Also need to reconstruct the `RefactoredData` object
+ // by combining all `affectedFiles` merge to new paths if applicable. Once implemented,
+ // just uncomment lines below and pass in the refactoredData.
+ // */
+ // // Tentative data structure to be built below, change inputs as needed but needs
+ // // to implement the `MultiRefactoredData` interface
+ // // For any temp files that need to be written due to merging, I'd suggest writing them all
+ // // to one temp directory and add that directory to allTempDirs, that way they will be removed
+ // // UNCOMMENT ME WHEN READY
+ // // const combinedRefactoredData: MultiRefactoredData = {
+ // // targetFile: combinedTargetFile,
+ // // affectedFiles: allAffectedFiles,
+ // // energySaved: totalEnergySaved,
+ // // tempDirs: allTempDirs
+ // // }
+ // // UNCOMMENT ME WHEN READY
+ // // startRefactoringSession(contextManager,editor,combinedRefactoredData);
+ // if (combinedRefactoredData) {
+ // // await RefactorManager.previewRefactor(editor, combinedRefactoredData);
+ // vscode.window.showInformationMessage(
+ // `Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(
+ // 4,
+ // )}`,
+ // );
+ // } else {
+ // vscode.window.showErrorMessage(
+ // 'Eco: Refactoring failed. See console for details.',
+ // );
+ // return;
+ // }
+ // if (allUpdatedSmells.length) {
+ // const fileHighlighter = new FileHighlighter(contextManager);
+ // fileHighlighter.highlightSmells(editor, allUpdatedSmells);
+ // } else {
+ // vscode.window.showWarningMessage(
+ // 'Eco: No updated smells detected after refactoring.',
+ // );
// }
-
- // UNCOMMENT ME WHEN READY
- // startRefactoringSession(contextManager,editor,combinedRefactoredData);
-
- if (combinedRefactoredData) {
- // await RefactorManager.previewRefactor(editor, combinedRefactoredData);
- vscode.window.showInformationMessage(
- `Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(
- 4,
- )}`,
- );
- } else {
- vscode.window.showErrorMessage(
- 'Eco: Refactoring failed. See console for details.',
- );
- return;
- }
-
- if (allUpdatedSmells.length) {
- const fileHighlighter = new FileHighlighter(contextManager);
- fileHighlighter.highlightSmells(editor, allUpdatedSmells);
- } else {
- vscode.window.showWarningMessage(
- 'Eco: No updated smells detected after refactoring.',
- );
- }
}
+/* istanbul ignore next */
async function startRefactoringSession(
contextManager: ContextManager,
editor: vscode.TextEditor,
@@ -324,7 +308,7 @@ async function startRefactoringSession(
sidebarState.isOpening = false;
}
-async function cleanTemps(pastData: any): Promise {
+export async function cleanTemps(pastData: any): Promise {
console.log('Cleaning up stale artifacts');
const tempDirs =
(pastData!.tempDir! as string) || (pastData!.tempDirs! as string[]);
diff --git a/test/commands/refactorSmell.test.ts b/test/commands/refactorSmell.test.ts
index 7702363..402c477 100644
--- a/test/commands/refactorSmell.test.ts
+++ b/test/commands/refactorSmell.test.ts
@@ -1,5 +1,7 @@
import * as vscode from 'vscode';
-import { refactorSelectedSmell } from '../../src/commands/refactorSmell';
+import * as fs from 'fs';
+
+import { refactorSelectedSmell, cleanTemps } from '../../src/commands/refactorSmell';
import { ContextManager } from '../../src/context/contextManager';
import { refactorSmell } from '../../src/api/backend';
import { FileHighlighter } from '../../src/ui/fileHighlighter';
@@ -31,7 +33,7 @@ jest.mock('vscode', () => ({
},
Uri: {
file: jest.fn((path) => ({
- toString: () => `file://${path}`,
+ toString: (): string => `file://${path}`,
fsPath: path,
})),
},
@@ -60,7 +62,7 @@ jest.mock('timers/promises', () => ({
setTimeout: jest.fn().mockResolvedValue(undefined),
}));
-describe('refactorSelectedSmell', () => {
+describe('refactorSmell', () => {
let mockContextManager: jest.Mocked;
let mockEditor: any;
let mockDocument: any;
@@ -118,180 +120,206 @@ describe('refactorSelectedSmell', () => {
(vscode.commands.executeCommand as jest.Mock).mockResolvedValue(undefined);
});
- test('should show error when no active editor', async () => {
- (vscode.window as any).activeTextEditor = undefined;
-
- await refactorSelectedSmell(mockContextManager);
+ describe('refactorSelectedSmell', () => {
+ it('should show error when no active editor', async () => {
+ (vscode.window as any).activeTextEditor = undefined;
- expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
- 'Eco: Unable to proceed as no active editor or file path found.',
- );
- });
+ await refactorSelectedSmell(mockContextManager);
- test('should show error when no smells detected', async () => {
- mockContextManager.getWorkspaceData.mockImplementation((key) => {
- if (key === envConfig.SMELL_MAP_KEY) {
- return {
- '/test/file.ts': {
- smells: [],
- },
- };
- }
- return null;
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: Unable to proceed as no active editor or file path found.',
+ );
});
- await refactorSelectedSmell(mockContextManager);
-
- expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
- 'Eco: No smells detected in the file for refactoring.',
- );
- });
-
- test('should show error when no matching smell found for selected line', async () => {
- const mockSmells = [createMockSmell(5)];
-
- mockContextManager.getWorkspaceData.mockImplementation((key) => {
- if (key === envConfig.SMELL_MAP_KEY) {
- return {
- '/test/file.ts': {
- smells: mockSmells,
- },
- };
- }
- return null;
+ it('should show error when no smells detected', async () => {
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: [],
+ },
+ };
+ }
+ return undefined;
+ });
+
+ await refactorSelectedSmell(mockContextManager);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: No smells detected in the file for refactoring.',
+ );
});
- await refactorSelectedSmell(mockContextManager);
-
- expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
- 'Eco: No matching smell found for refactoring.',
- );
- });
+ it('should show error when no matching smell found for selected line', async () => {
+ const mockSmells = [createMockSmell(5)];
+
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: mockSmells,
+ },
+ };
+ }
+ return undefined;
+ });
+
+ await refactorSelectedSmell(mockContextManager);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: No matching smell found for refactoring.',
+ );
+ });
- test('should successfully refactor a smell when found', async () => {
- const mockSmells = [createMockSmell(1)];
+ it('should successfully refactor a smell when found', async () => {
+ const mockSmells = [createMockSmell(1)];
- const mockRefactorResult = {
- refactoredData: {
- tempDir: '/tmp/test',
- targetFile: {
- original: '/test/file.ts',
- refactored: '/test/file.refactored.ts',
+ const mockRefactorResult = {
+ refactoredData: {
+ tempDir: '/tmp/test',
+ targetFile: {
+ original: '/test/file.ts',
+ refactored: '/test/file.refactored.ts',
+ },
+ affectedFiles: [
+ {
+ original: '/test/other.ts',
+ refactored: '/test/other.refactored.ts',
+ },
+ ],
+ energySaved: 10,
},
- affectedFiles: [
+ updatedSmells: [
{
- original: '/test/other.ts',
- refactored: '/test/other.refactored.ts',
+ ...createMockSmell(1),
+ messageId: 'updated-smell',
+ symbol: 'UpdatedSmell',
+ message: 'Updated message',
},
],
- energySaved: 10,
- },
- updatedSmells: [
- {
- ...createMockSmell(1),
- messageId: 'updated-smell',
- symbol: 'UpdatedSmell',
- message: 'Updated message',
- },
- ],
- };
-
- mockContextManager.getWorkspaceData.mockImplementation((key) => {
- if (key === envConfig.SMELL_MAP_KEY) {
- return {
- '/test/file.ts': {
- smells: mockSmells,
- },
- };
- }
- return null;
+ };
+
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: mockSmells,
+ },
+ };
+ }
+ return undefined;
+ });
+
+ (refactorSmell as jest.Mock).mockResolvedValue(mockRefactorResult);
+
+ await refactorSelectedSmell(mockContextManager);
+
+ expect(vscode.workspace.save).toHaveBeenCalled();
+ expect(refactorSmell).toHaveBeenCalledWith('/test/file.ts', mockSmells[0]);
+ expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
+ 'Refactoring report available in sidebar.',
+ );
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
+ 'extension.refactorSidebar.focus',
+ );
+ expect(vscode.workspace.openTextDocument).toHaveBeenCalled();
+ expect(vscode.window.showTextDocument).toHaveBeenCalled();
+ expect(FileHighlighter).toHaveBeenCalled();
});
- (refactorSmell as jest.Mock).mockResolvedValue(mockRefactorResult);
-
- await refactorSelectedSmell(mockContextManager);
-
- expect(vscode.workspace.save).toHaveBeenCalled();
- expect(refactorSmell).toHaveBeenCalledWith('/test/file.ts', mockSmells[0]);
- expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
- 'Refactoring report available in sidebar.',
- );
- expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
- 'extension.refactorSidebar.focus',
- );
- expect(vscode.workspace.openTextDocument).toHaveBeenCalled();
- expect(vscode.window.showTextDocument).toHaveBeenCalled();
- expect(FileHighlighter).toHaveBeenCalled();
- });
-
- test('should handle refactoring failure', async () => {
- const mockSmells = [createMockSmell(1)];
+ it('should handle refactoring failure', async () => {
+ const mockSmells = [createMockSmell(1)];
+
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: mockSmells,
+ },
+ };
+ }
+ return undefined;
+ });
+
+ (refactorSmell as jest.Mock).mockRejectedValue(
+ new Error('Refactoring failed'),
+ );
+
+ await refactorSelectedSmell(mockContextManager);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: Refactoring failed. See console for details.',
+ );
+ });
- mockContextManager.getWorkspaceData.mockImplementation((key) => {
- if (key === envConfig.SMELL_MAP_KEY) {
- return {
- '/test/file.ts': {
- smells: mockSmells,
+ it('should handle given smell parameter', async () => {
+ const givenSmell = createMockSmell(3);
+ const mockSmells = [givenSmell];
+
+ mockContextManager.getWorkspaceData.mockImplementation((key) => {
+ if (key === envConfig.SMELL_MAP_KEY) {
+ return {
+ '/test/file.ts': {
+ smells: mockSmells,
+ },
+ };
+ }
+ return undefined;
+ });
+
+ const mockRefactorResult = {
+ refactoredData: {
+ tempDir: '/tmp/test',
+ targetFile: {
+ original: '/test/file.ts',
+ refactored: '/test/file.refactored.ts',
},
- };
- }
- return null;
+ affectedFiles: [
+ {
+ original: '/test/other.ts',
+ refactored: '/test/other.refactored.ts',
+ },
+ ],
+ energySaved: 10,
+ },
+ updatedSmells: [],
+ };
+
+ (refactorSmell as jest.Mock).mockResolvedValue(mockRefactorResult);
+
+ await refactorSelectedSmell(mockContextManager, givenSmell);
+
+ expect(refactorSmell).toHaveBeenCalledWith('/test/file.ts', givenSmell);
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
+ 'extension.refactorSidebar.focus',
+ );
+ expect(vscode.workspace.openTextDocument).toHaveBeenCalled();
+ expect(vscode.window.showTextDocument).toHaveBeenCalled();
+ expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(
+ 'Eco: No updated smells detected after refactoring.',
+ );
});
+ });
- (refactorSmell as jest.Mock).mockRejectedValue(new Error('Refactoring failed'));
-
- await refactorSelectedSmell(mockContextManager);
+ describe('Clean Temp Directory', () => {
+ it('removes one temp directory', async () => {
+ const mockPastData = { tempDir: 'mock/temp/dir' };
- expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
- 'Eco: Refactoring failed. See console for details.',
- );
- });
+ jest.spyOn(fs.promises, 'rm').mockResolvedValueOnce();
- test('should handle given smell parameter', async () => {
- const givenSmell = createMockSmell(3);
- const mockSmells = [givenSmell];
+ await cleanTemps(mockPastData);
- mockContextManager.getWorkspaceData.mockImplementation((key) => {
- if (key === envConfig.SMELL_MAP_KEY) {
- return {
- '/test/file.ts': {
- smells: mockSmells,
- },
- };
- }
- return null;
+ expect(fs.promises.rm).toHaveBeenCalled();
});
- const mockRefactorResult = {
- refactoredData: {
- tempDir: '/tmp/test',
- targetFile: {
- original: '/test/file.ts',
- refactored: '/test/file.refactored.ts',
- },
- affectedFiles: [
- {
- original: '/test/other.ts',
- refactored: '/test/other.refactored.ts',
- },
- ],
- energySaved: 10,
- },
- updatedSmells: [],
- };
+ it('removes multiple temp directory', async () => {
+ const mockPastData = { tempDirs: ['mock/temp/dir1', 'mock/temp/dir2'] };
- (refactorSmell as jest.Mock).mockResolvedValue(mockRefactorResult);
+ jest.spyOn(fs.promises, 'rm').mockResolvedValueOnce();
- await refactorSelectedSmell(mockContextManager, givenSmell);
+ await cleanTemps(mockPastData);
- expect(refactorSmell).toHaveBeenCalledWith('/test/file.ts', givenSmell);
- expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
- 'extension.refactorSidebar.focus',
- );
- expect(vscode.workspace.openTextDocument).toHaveBeenCalled();
- expect(vscode.window.showTextDocument).toHaveBeenCalled();
- expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(
- 'Eco: No updated smells detected after refactoring.',
- );
+ expect(fs.promises.rm).toHaveBeenCalledTimes(2);
+ });
});
});
From b42eb10e0349521d509b0ec993a8aa991022862f Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sun, 16 Mar 2025 19:04:33 -0400
Subject: [PATCH 73/83] updated file coverage config
---
package.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 9a1d884..36c4ed4 100644
--- a/package.json
+++ b/package.json
@@ -210,7 +210,8 @@
"!test/mocks/*",
"!src/extension.ts",
"!src/context/*",
- "!src/utils/configManager.ts"
+ "!src/utils/configManager.ts",
+ "!src/commands/showLogs.ts"
]
},
"lint-staged": {
From b39ac41a5f5dcc3fb3c812a943bc06e3265d027e Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 17 Mar 2025 18:03:54 -0400
Subject: [PATCH 74/83] temporarily omitting sidebar files from coverage
---
package.json | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 36c4ed4..6fb5228 100644
--- a/package.json
+++ b/package.json
@@ -211,7 +211,9 @@
"!src/extension.ts",
"!src/context/*",
"!src/utils/configManager.ts",
- "!src/commands/showLogs.ts"
+ "!src/commands/showLogs.ts",
+ "!src/ui/refactorView.ts",
+ "!src/utils/handleEditorChange.ts"
]
},
"lint-staged": {
From d3998836da8bd8677f695727d790f14c26c89099 Mon Sep 17 00:00:00 2001
From: mya
Date: Mon, 17 Mar 2025 18:57:08 -0400
Subject: [PATCH 75/83] Moved changes to new-toggle-button branch
---
package-lock.json | 12 ++++++-
package.json | 32 ++++++++++++++++++-
src/commands/detectSmells.ts | 6 +++-
src/commands/refactorSmell.ts | 4 +--
src/commands/toggleSmellLinting.ts | 50 +++++++++++++++++++++++++++++
src/extension.ts | 51 ++++++++++++++++++++++++++++++
src/ui/fileHighlighter.ts | 31 ++++++++----------
src/utils/envConfig.ts | 3 ++
test/mocks/env-config-mock.ts | 1 +
test/ui/fileHighlighter.test.ts | 2 +-
10 files changed, 168 insertions(+), 24 deletions(-)
create mode 100644 src/commands/toggleSmellLinting.ts
diff --git a/package-lock.json b/package-lock.json
index aee0627..c290400 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,7 +40,8 @@
"ts-loader": "^9.5.1",
"typescript": "^5.7.2",
"webpack": "^5.95.0",
- "webpack-cli": "^5.1.4"
+ "webpack-cli": "^5.1.4",
+ "webpack-node-externals": "^3.0.0"
},
"engines": {
"vscode": "^1.92.0"
@@ -8417,6 +8418,15 @@
"node": ">=10.0.0"
}
},
+ "node_modules/webpack-node-externals": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz",
+ "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
diff --git a/package.json b/package.json
index abe3f28..138f185 100644
--- a/package.json
+++ b/package.json
@@ -52,8 +52,37 @@
"title": "Show Backend Logs",
"category": "Eco",
"enablement": "false"
+ },
+ {
+ "command": "eco.toggleSmellLinting",
+ "title": "π Toggle Smell Linting",
+ "category": "Eco"
}
+
],
+ "menus": {
+ "editor/title": [
+ {
+ "command": "eco.toggleSmellLinting",
+ "group": "navigation",
+ "when": "eco.smellLintingEnabled == false",
+ "icon": {
+ "light": "off.svg",
+ "dark": "off.svg"
+ }
+
+ },
+ {
+ "command": "eco.toggleSmellLinting",
+ "group": "navigation",
+ "when": "eco.smellLintingEnabled == true",
+ "icon": {
+ "light": "on.svg",
+ "dark": "on.svg"
+ }
+ }
+ ]
+ },
"configuration": {
"title": "EcoOptimizer",
"properties": {
@@ -244,7 +273,8 @@
"ts-loader": "^9.5.1",
"typescript": "^5.7.2",
"webpack": "^5.95.0",
- "webpack-cli": "^5.1.4"
+ "webpack-cli": "^5.1.4",
+ "webpack-node-externals": "^3.0.0"
},
"dependencies": {
"@types/dotenv": "^6.1.1",
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index b8d2f0c..02f5223 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -106,13 +106,17 @@ export async function detectSmells(contextManager: ContextManager): Promise {
+ const isEnabled = contextManager.getWorkspaceData(
+ envConfig.SMELL_LINTING_ENABLED_KEY,
+ false,
+ );
+ const newState = !isEnabled;
+
+ // Update state immediately for UI responsiveness
+ vscode.commands.executeCommand('setContext', 'eco.smellLintingEnabled', newState);
+
+ // Use the singleton instance of FileHighlighter
+ const fileHighlighter = FileHighlighter.getInstance(contextManager);
+
+ try {
+ if (newState) {
+ // Run detection and update state on success
+ await detectSmells(contextManager);
+
+ await contextManager.setWorkspaceData(
+ envConfig.SMELL_LINTING_ENABLED_KEY,
+ newState,
+ );
+ } else {
+ // Clear highlights and update state
+ fileHighlighter.resetHighlights(); // Call resetHighlights on the singleton instance
+ await contextManager.setWorkspaceData(
+ envConfig.SMELL_LINTING_ENABLED_KEY,
+ newState,
+ );
+ vscode.window.showInformationMessage('Eco: Smell linting turned off.');
+ }
+ } catch (error) {
+ console.error('Eco: Error toggling smell linting:', error);
+ vscode.window.showErrorMessage('Eco: Failed to toggle smell linting.');
+ // Ensure UI state matches actual on error
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'eco.smellLintingEnabled',
+ isEnabled,
+ );
+ }
+}
diff --git a/src/extension.ts b/src/extension.ts
index 9560813..0972792 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -20,6 +20,8 @@ import { LineSelectionManager } from './ui/lineSelectionManager';
import { checkServerStatus } from './api/backend';
import { serverStatus } from './utils/serverStatus';
+import { toggleSmellLinting } from './commands/toggleSmellLinting';
+
export const globalData: { contextManager?: ContextManager } = {
contextManager: undefined,
};
@@ -114,6 +116,14 @@ export function activate(context: vscode.ExtensionContext): void {
),
);
+ // screen button go brr
+ context.subscriptions.push(
+ vscode.commands.registerCommand('eco.toggleSmellLinting', () => {
+ console.log('Eco: Toggle Smell Linting Command Triggered');
+ toggleSmellLinting(contextManager);
+ }),
+ );
+
// ===============================================================
// REGISTER VIEWS
// ===============================================================
@@ -194,6 +204,47 @@ export function activate(context: vscode.ExtensionContext): void {
}),
);
+ // Listen for file save events
+ context.subscriptions.push(
+ vscode.workspace.onDidSaveTextDocument(async (document) => {
+ console.log('Eco: Detected document saved event');
+
+ // Check if smell linting is enabled
+ const isEnabled = contextManager.getWorkspaceData(
+ envConfig.SMELL_LINTING_ENABLED_KEY,
+ false,
+ );
+ if (isEnabled) {
+ console.log('Eco: Smell linting is enabled. Detecting smells...');
+ await detectSmells(contextManager);
+ }
+ }),
+ );
+
+ // Listen for editor changes
+ context.subscriptions.push(
+ vscode.window.onDidChangeActiveTextEditor(async (editor) => {
+ if (editor) {
+ console.log('Eco: Detected editor change event');
+
+ // Check if the file is a Python file
+ if (editor.document.languageId === 'python') {
+ console.log('Eco: Active file is a Python file.');
+
+ // Check if smell linting is enabled
+ const isEnabled = contextManager.getWorkspaceData(
+ envConfig.SMELL_LINTING_ENABLED_KEY,
+ false,
+ );
+ if (isEnabled) {
+ console.log('Eco: Smell linting is enabled. Detecting smells...');
+ await detectSmells(contextManager);
+ }
+ }
+ }
+ }),
+ );
+
// ===============================================================
// HANDLE SMELL FILTER CHANGES
// ===============================================================
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index be9fd82..09cdf55 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -5,19 +5,28 @@ import { HoverManager } from './hoverManager';
import { SMELL_MAP } from '../utils/smellDetails';
export class FileHighlighter {
- private contextManager;
+ private static instance: FileHighlighter;
+ private contextManager: ContextManager;
private decorations: vscode.TextEditorDecorationType[] = [];
- public constructor(contextManager: ContextManager) {
+ private constructor(contextManager: ContextManager) {
this.contextManager = contextManager;
}
+ public static getInstance(contextManager: ContextManager): FileHighlighter {
+ if (!FileHighlighter.instance) {
+ FileHighlighter.instance = new FileHighlighter(contextManager);
+ }
+ return FileHighlighter.instance;
+ }
+
public resetHighlights(): void {
if (this.decorations.length > 0) {
console.log('Removing decorations');
this.decorations.forEach((decoration) => {
decoration.dispose();
});
+ this.decorations = []; // Clear the decorations array
}
}
@@ -66,17 +75,6 @@ export class FileHighlighter {
}
private getDecoration(color: string): vscode.TextEditorDecorationType {
- // ================= EXTRA DECORATIONS ===========================
- const _underline = vscode.window.createTextEditorDecorationType({
- textDecoration: `wavy ${color} underline 1px`,
- });
-
- const _flashlight = vscode.window.createTextEditorDecorationType({
- isWholeLine: true,
- backgroundColor: color,
- });
- // ================================================================
-
const aLittleExtra = vscode.window.createTextEditorDecorationType({
borderWidth: '1px 2px 1px 0', // Top, Right, Bottom, No Left border
borderStyle: 'solid',
@@ -91,11 +89,8 @@ export class FileHighlighter {
overviewRulerLane: vscode.OverviewRulerLane.Right,
});
- const decoration = aLittleExtra; // Select decoration
-
- this.decorations.push(decoration);
-
- return decoration;
+ this.decorations.push(aLittleExtra); // Add the decoration to the list
+ return aLittleExtra;
}
}
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts
index 4808865..60bd31d 100644
--- a/src/utils/envConfig.ts
+++ b/src/utils/envConfig.ts
@@ -9,6 +9,7 @@ export interface EnvConfig {
LAST_USED_SMELLS_KEY?: string;
CURRENT_REFACTOR_DATA_KEY?: string;
ACTIVE_DIFF_KEY?: string;
+ SMELL_LINTING_ENABLED_KEY: string;
}
export const envConfig: EnvConfig = {
@@ -18,4 +19,6 @@ export const envConfig: EnvConfig = {
LAST_USED_SMELLS_KEY: process.env.LAST_USED_SMELLS_KEY,
CURRENT_REFACTOR_DATA_KEY: process.env.CURRENT_REFACTOR_DATA_KEY,
ACTIVE_DIFF_KEY: process.env.ACTIVE_DIFF_KEY,
+ SMELL_LINTING_ENABLED_KEY:
+ process.env.SMELL_LINTING_ENABLED_KEY || 'eco.smellLintingEnabled',
};
diff --git a/test/mocks/env-config-mock.ts b/test/mocks/env-config-mock.ts
index 48aad7b..c108fc2 100644
--- a/test/mocks/env-config-mock.ts
+++ b/test/mocks/env-config-mock.ts
@@ -8,6 +8,7 @@ jest.mock('../../src/utils/envConfig', () => {
LAST_USED_SMELLS_KEY: 'last-used-smells-key',
CURRENT_REFACTOR_DATA_KEY: 'current-refactor-data-key',
ACTIVE_DIFF_KEY: 'active-diff-key',
+ SMELL_LINTING_ENABLED_KEY: 'smellLintingEnabledKey',
};
return { envConfig: mockEnvConfig };
diff --git a/test/ui/fileHighlighter.test.ts b/test/ui/fileHighlighter.test.ts
index 32fceda..f166066 100644
--- a/test/ui/fileHighlighter.test.ts
+++ b/test/ui/fileHighlighter.test.ts
@@ -20,7 +20,7 @@ describe('File Highlighter', () => {
setWorkspaceData: jest.fn(),
} as unknown as ContextManager;
- fileHighlighter = new FileHighlighter(contextManagerMock);
+ fileHighlighter = FileHighlighter.getInstance(contextManagerMock);
});
it('should create decorations', () => {
From 87c1f72e62e2c083a2f40c167051a546cf593fdd Mon Sep 17 00:00:00 2001
From: mya
Date: Mon, 17 Mar 2025 19:05:43 -0400
Subject: [PATCH 76/83] Closes ssm-lab/capstone--source-code-optimizer#405
---
src/commands/toggleSmellLinting.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/commands/toggleSmellLinting.ts b/src/commands/toggleSmellLinting.ts
index a156eda..d4b4f54 100644
--- a/src/commands/toggleSmellLinting.ts
+++ b/src/commands/toggleSmellLinting.ts
@@ -22,7 +22,7 @@ export async function toggleSmellLinting(
try {
if (newState) {
// Run detection and update state on success
- await detectSmells(contextManager);
+ await detectSmells(contextManager); // in the future recieve a true/false
await contextManager.setWorkspaceData(
envConfig.SMELL_LINTING_ENABLED_KEY,
From cdb98b2a47ab30371db2692be92fa3b2aa5b6f4e Mon Sep 17 00:00:00 2001
From: mya
Date: Mon, 17 Mar 2025 19:22:57 -0400
Subject: [PATCH 77/83] Add file path to avoid tests
---
package.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 218265d..9b1eb7e 100644
--- a/package.json
+++ b/package.json
@@ -242,7 +242,8 @@
"!src/utils/configManager.ts",
"!src/commands/showLogs.ts",
"!src/ui/refactorView.ts",
- "!src/utils/handleEditorChange.ts"
+ "!src/utils/handleEditorChange.ts",
+ "!src/commands/toggleSmellLinting.ts"
]
},
"lint-staged": {
From f1139767cc0a813dfcf5b4c47e0e4188cdc89d1f Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon, 17 Mar 2025 20:37:01 -0400
Subject: [PATCH 78/83] fixed failing tests + added test for new detection
toggle module
fixes ssm-lab/capstone--source-code-optimizer#504
---
package.json | 37 +++++----
test/commands/detectSmells.test.ts | 10 +++
test/commands/refactorSmell.test.ts | 14 ++--
test/commands/toggleSmellLinting.test.ts | 96 ++++++++++++++++++++++++
test/mocks/vscode-mock.ts | 1 +
test/ui/fileHighlighter.test.ts | 34 ++++-----
6 files changed, 147 insertions(+), 45 deletions(-)
create mode 100644 test/commands/toggleSmellLinting.test.ts
diff --git a/package.json b/package.json
index 9b1eb7e..cecf9a4 100644
--- a/package.json
+++ b/package.json
@@ -58,28 +58,26 @@
"title": "π Toggle Smell Linting",
"category": "Eco"
}
-
],
- "menus": {
- "editor/title": [
+ "menus": {
+ "editor/title": [
{
- "command": "eco.toggleSmellLinting",
- "group": "navigation",
- "when": "eco.smellLintingEnabled == false",
- "icon": {
- "light": "off.svg",
- "dark": "off.svg"
- }
-
+ "command": "eco.toggleSmellLinting",
+ "group": "navigation",
+ "when": "eco.smellLintingEnabled == false",
+ "icon": {
+ "light": "off.svg",
+ "dark": "off.svg"
+ }
},
{
- "command": "eco.toggleSmellLinting",
- "group": "navigation",
- "when": "eco.smellLintingEnabled == true",
- "icon": {
- "light": "on.svg",
- "dark": "on.svg"
- }
+ "command": "eco.toggleSmellLinting",
+ "group": "navigation",
+ "when": "eco.smellLintingEnabled == true",
+ "icon": {
+ "light": "on.svg",
+ "dark": "on.svg"
+ }
}
]
},
@@ -242,8 +240,7 @@
"!src/utils/configManager.ts",
"!src/commands/showLogs.ts",
"!src/ui/refactorView.ts",
- "!src/utils/handleEditorChange.ts",
- "!src/commands/toggleSmellLinting.ts"
+ "!src/utils/handleEditorChange.ts"
]
},
"lint-staged": {
diff --git a/test/commands/detectSmells.test.ts b/test/commands/detectSmells.test.ts
index e76b3eb..3e9e3d9 100644
--- a/test/commands/detectSmells.test.ts
+++ b/test/commands/detectSmells.test.ts
@@ -11,6 +11,7 @@ import * as editorUtils from '../../src/utils/editorUtils';
import { detectSmells } from '../../src/commands/detectSmells';
import { serverStatus, ServerStatusType } from '../../src/utils/serverStatus';
import { wipeWorkCache } from '../../src/commands/wipeWorkCache';
+import { envConfig } from '../../src/utils/envConfig';
jest.mock('../../src/commands/wipeWorkCache', () => ({
wipeWorkCache: jest.fn(),
@@ -227,5 +228,14 @@ describe('detectSmells', () => {
);
expect(mockHighlightSmells).toHaveBeenCalled();
+ expect(contextManagerMock.setWorkspaceData).toHaveBeenCalledWith(
+ envConfig.SMELL_LINTING_ENABLED_KEY,
+ true,
+ );
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
+ 'setContext',
+ 'eco.smellLintingEnabled',
+ true,
+ );
});
});
diff --git a/test/commands/refactorSmell.test.ts b/test/commands/refactorSmell.test.ts
index 402c477..0cfb772 100644
--- a/test/commands/refactorSmell.test.ts
+++ b/test/commands/refactorSmell.test.ts
@@ -50,13 +50,6 @@ jest.mock('../../src/api/backend', () => ({
refactorSmell: jest.fn(),
}));
-// mock FileHighlighter
-jest.mock('../../src/ui/fileHighlighter', () => ({
- FileHighlighter: jest.fn().mockImplementation(() => ({
- highlightSmells: jest.fn(),
- })),
-}));
-
// mock setTimeout
jest.mock('timers/promises', () => ({
setTimeout: jest.fn().mockResolvedValue(undefined),
@@ -64,6 +57,7 @@ jest.mock('timers/promises', () => ({
describe('refactorSmell', () => {
let mockContextManager: jest.Mocked;
+ let fileHighlighterSpy: jest.SpyInstance;
let mockEditor: any;
let mockDocument: any;
let mockSelection: any;
@@ -107,6 +101,10 @@ describe('refactorSmell', () => {
uri: { fsPath: '/test/file.ts' },
};
+ fileHighlighterSpy = jest.spyOn(FileHighlighter, 'getInstance').mockReturnValue({
+ highlightSmells: jest.fn(),
+ } as any);
+
// setup mock editor
mockEditor = {
document: mockDocument,
@@ -224,7 +222,7 @@ describe('refactorSmell', () => {
);
expect(vscode.workspace.openTextDocument).toHaveBeenCalled();
expect(vscode.window.showTextDocument).toHaveBeenCalled();
- expect(FileHighlighter).toHaveBeenCalled();
+ expect(fileHighlighterSpy).toHaveBeenCalled();
});
it('should handle refactoring failure', async () => {
diff --git a/test/commands/toggleSmellLinting.test.ts b/test/commands/toggleSmellLinting.test.ts
new file mode 100644
index 0000000..8aefb2a
--- /dev/null
+++ b/test/commands/toggleSmellLinting.test.ts
@@ -0,0 +1,96 @@
+import * as vscode from 'vscode';
+import { ContextManager } from '../../src/context/contextManager';
+import { toggleSmellLinting } from '../../src/commands/toggleSmellLinting';
+import { FileHighlighter } from '../../src/ui/fileHighlighter';
+import { detectSmells } from '../../src/commands/detectSmells';
+import { envConfig } from '../../src/utils/envConfig';
+
+jest.mock('../../src/commands/detectSmells', () => ({
+ detectSmells: jest.fn(),
+}));
+
+jest.mock('../../src/ui/fileHighlighter', () => ({
+ FileHighlighter: {
+ getInstance: jest.fn(),
+ },
+}));
+
+describe('toggleSmellLinting', () => {
+ let contextManagerMock: ContextManager;
+ let fileHighlighterMock: FileHighlighter;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ contextManagerMock = {
+ getWorkspaceData: jest.fn(),
+ setWorkspaceData: jest.fn(),
+ } as unknown as ContextManager;
+
+ fileHighlighterMock = {
+ resetHighlights: jest.fn(),
+ } as unknown as FileHighlighter;
+
+ (FileHighlighter.getInstance as jest.Mock).mockReturnValue(fileHighlighterMock);
+ });
+
+ it('should toggle from disabled to enabled state', async () => {
+ (contextManagerMock.getWorkspaceData as jest.Mock).mockReturnValue(false);
+
+ await toggleSmellLinting(contextManagerMock);
+
+ expect(detectSmells).toHaveBeenCalledWith(contextManagerMock);
+
+ expect(contextManagerMock.setWorkspaceData).toHaveBeenCalledWith(
+ envConfig.SMELL_LINTING_ENABLED_KEY,
+ true,
+ );
+
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
+ 'setContext',
+ 'eco.smellLintingEnabled',
+ true,
+ );
+ });
+
+ it('should toggle from enabled to disabled state', async () => {
+ (contextManagerMock.getWorkspaceData as jest.Mock).mockReturnValue(true);
+
+ await toggleSmellLinting(contextManagerMock);
+
+ expect(fileHighlighterMock.resetHighlights).toHaveBeenCalled();
+
+ expect(contextManagerMock.setWorkspaceData).toHaveBeenCalledWith(
+ envConfig.SMELL_LINTING_ENABLED_KEY,
+ false,
+ );
+
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
+ 'setContext',
+ 'eco.smellLintingEnabled',
+ false,
+ );
+
+ expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
+ 'Eco: Smell linting turned off.',
+ );
+ });
+
+ it('should handle errors and revert UI state', async () => {
+ (contextManagerMock.getWorkspaceData as jest.Mock).mockReturnValue(false);
+
+ (detectSmells as jest.Mock).mockRejectedValue(new Error('Test error'));
+
+ await toggleSmellLinting(contextManagerMock);
+
+ expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
+ 'Eco: Failed to toggle smell linting.',
+ );
+
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
+ 'setContext',
+ 'eco.smellLintingEnabled',
+ false,
+ );
+ });
+});
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
index 7716eb4..7b36a8f 100644
--- a/test/mocks/vscode-mock.ts
+++ b/test/mocks/vscode-mock.ts
@@ -109,6 +109,7 @@ export const languages = {
export const commands = {
registerCommand: jest.fn(),
+ executeCommand: jest.fn(),
};
// Mock VS Code classes
diff --git a/test/ui/fileHighlighter.test.ts b/test/ui/fileHighlighter.test.ts
index f166066..74eaf20 100644
--- a/test/ui/fileHighlighter.test.ts
+++ b/test/ui/fileHighlighter.test.ts
@@ -23,19 +23,10 @@ describe('File Highlighter', () => {
fileHighlighter = FileHighlighter.getInstance(contextManagerMock);
});
- it('should create decorations', () => {
- const color = 'red';
- const decoration = fileHighlighter['getDecoration'](color);
-
- // Assert decoration was created
- expect(vscode.window.createTextEditorDecorationType).toHaveBeenCalled();
- expect(decoration).toBeDefined();
- });
-
- it('should highlight smells', () => {
+ it('should not reset highlight decorations on first init', () => {
const smells = [
{
- messageId: 'smell1',
+ messageId: 'R1729',
occurences: [{ line: 1 }],
},
] as unknown as Smell[];
@@ -44,16 +35,25 @@ describe('File Highlighter', () => {
hoverContent: 'hover content' as unknown as MarkdownString,
} as unknown as HoverManager);
- fileHighlighter.highlightSmell(vscode.window.activeTextEditor, smells, 'R1729');
+ fileHighlighter.highlightSmells(vscode.window.activeTextEditor, smells);
// Assert decorations were set
- expect(vscode.window.activeTextEditor.setDecorations).toHaveBeenCalled();
+ expect(fileHighlighter['decorations'][0].dispose).not.toHaveBeenCalled();
});
- it('should not reset highlight decorations on first init', () => {
+ it('should create decorations', () => {
+ const color = 'red';
+ const decoration = fileHighlighter['getDecoration'](color);
+
+ // Assert decoration was created
+ expect(vscode.window.createTextEditorDecorationType).toHaveBeenCalled();
+ expect(decoration).toBeDefined();
+ });
+
+ it('should highlight smells', () => {
const smells = [
{
- messageId: 'R1729',
+ messageId: 'smell1',
occurences: [{ line: 1 }],
},
] as unknown as Smell[];
@@ -62,10 +62,10 @@ describe('File Highlighter', () => {
hoverContent: 'hover content' as unknown as MarkdownString,
} as unknown as HoverManager);
- fileHighlighter.highlightSmells(vscode.window.activeTextEditor, smells);
+ fileHighlighter.highlightSmell(vscode.window.activeTextEditor, smells, 'R1729');
// Assert decorations were set
- expect(fileHighlighter['decorations'][0].dispose).not.toHaveBeenCalled();
+ expect(vscode.window.activeTextEditor.setDecorations).toHaveBeenCalled();
});
it('should reset highlight decorations on subsequent calls', () => {
From 55fb8ffdbf0a820bf3cc7d05592e27f22de64c0c Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Tue, 18 Mar 2025 11:42:05 -0400
Subject: [PATCH 79/83] [DEV] Add Smell Customization (#8)
* Enabled customization of smell highlights
closes ssm-lab/capstone--source-code-optimizer#500
* fixed up smell settings order and descriptions
---
CHANGELOG.md | 2 +-
package-lock.json | 4 +-
package.json | 238 ++++++++++++++++++------
src/commands/detectSmells.ts | 7 +-
src/commands/refactorSmell.ts | 242 ++++++++++++-------------
src/extension.ts | 74 ++++----
src/global.d.ts | 1 -
src/ui/fileHighlighter.ts | 105 ++++++-----
src/utils/configManager.ts | 12 +-
src/utils/handleEditorChange.ts | 6 +-
src/utils/handleSmellSettings.ts | 17 +-
src/utils/smellDetails.ts | 27 +--
test/commands/detectSmells.test.ts | 32 ++--
test/mocks/vscode-mock.ts | 5 +-
test/ui/fileHighlighter.test.ts | 46 ++++-
test/utils/handleSmellSettings.test.ts | 49 +++--
16 files changed, 517 insertions(+), 350 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f03f0e..f977202 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Change Log
-All notable changes to the "ecooptimizer-vs-code-plugin" extension will be documented in this file.
+All notable changes to the "ecooptimizer" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
diff --git a/package-lock.json b/package-lock.json
index c290400..0688744 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "ecooptimizer-vs-code-plugin",
+ "name": "ecooptimizer",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "ecooptimizer-vs-code-plugin",
+ "name": "ecooptimizer",
"version": "0.0.1",
"dependencies": {
"@types/dotenv": "^6.1.1",
diff --git a/package.json b/package.json
index cecf9a4..a338566 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,13 @@
{
- "name": "ecooptimizer-vs-code-plugin",
+ "name": "ecooptimizer",
"displayName": "EcoOptimizer VS Code Plugin",
+ "contributors": [
+ "Sevhena Walker",
+ "Tanveer Brar",
+ "Ayushi Amin",
+ "Mya Hussain",
+ "Nivetah Kuruparan"
+ ],
"description": "VS Code Plugin for EcoOptimizer Refactoring Tool",
"version": "0.0.1",
"engines": {
@@ -16,45 +23,45 @@
"contributes": {
"commands": [
{
- "command": "ecooptimizer-vs-code-plugin.detectSmells",
+ "command": "ecooptimizer.detectSmells",
"title": "Detect Smells",
"category": "Eco"
},
{
- "command": "ecooptimizer-vs-code-plugin.refactorSmell",
+ "command": "ecooptimizer.refactorSmell",
"title": "Refactor Smell",
"category": "Eco"
},
{
- "command": "ecooptimizer-vs-code-plugin.wipeWorkCache",
+ "command": "ecooptimizer.wipeWorkCache",
"title": "Wipe Workspace Cache",
"category": "Eco"
},
{
- "command": "ecooptimizer-vs-code-plugin.showRefactorSidebar",
+ "command": "ecooptimizer.showRefactorSidebar",
"title": "Show Refactor Sidebar",
"category": "Eco"
},
{
- "command": "ecooptimizer-vs-code-plugin.pauseRefactorSidebar",
+ "command": "ecooptimizer.pauseRefactorSidebar",
"title": "Pause Refactor Sidebar",
"category": "Eco",
"enablement": "false"
},
{
- "command": "ecooptimizer-vs-code-plugin.clearRefactorSidebar",
+ "command": "ecooptimizer.clearRefactorSidebar",
"title": "Clear Refactor Sidebar",
"category": "Eco",
"enablement": "false"
},
{
- "command": "ecooptimizer-vs-code-plugin.startLogging",
+ "command": "ecooptimizer.startLogging",
"title": "Show Backend Logs",
"category": "Eco",
"enablement": "false"
},
{
- "command": "eco.toggleSmellLinting",
+ "command": "ecooptimizer.toggleSmellLinting",
"title": "π Toggle Smell Linting",
"category": "Eco"
}
@@ -62,7 +69,7 @@
"menus": {
"editor/title": [
{
- "command": "eco.toggleSmellLinting",
+ "command": "ecooptimizer.toggleSmellLinting",
"group": "navigation",
"when": "eco.smellLintingEnabled == false",
"icon": {
@@ -71,7 +78,7 @@
}
},
{
- "command": "eco.toggleSmellLinting",
+ "command": "ecooptimizer.toggleSmellLinting",
"group": "navigation",
"when": "eco.smellLintingEnabled == true",
"icon": {
@@ -94,74 +101,201 @@
"default": "",
"description": "Path to store log files and output reports. Defaults to a 'logs' folder inside the workspace."
},
- "ecooptimizer.enableSmells": {
+ "detection.smells": {
+ "order": 1,
"type": "object",
"additionalProperties": false,
- "description": "Enable or disable specific code smell detections.",
+ "description": "Configure which smells to detect and their highlight colours.",
"default": {
- "long-element-chain": true,
- "too-many-arguments": true,
- "long-lambda-expression": true,
- "long-message-chain": true,
- "unused-variables-and-attributes": true,
- "cached-repeated-calls": true,
- "string-concat-loop": true,
- "no-self-use": true,
- "use-a-generator": true
+ "long-element-chain": {
+ "enabled": true,
+ "colour": "lightblue"
+ },
+ "too-many-arguments": {
+ "enabled": true,
+ "colour": "lightcoral"
+ },
+ "long-lambda-expression": {
+ "enabled": true,
+ "colour": "mediumpurple"
+ },
+ "long-message-chain": {
+ "enabled": true,
+ "colour": "lightpink"
+ },
+ "cached-repeated-calls": {
+ "enabled": true,
+ "colour": "lightgreen"
+ },
+ "string-concat-loop": {
+ "enabled": true,
+ "colour": "lightsalmon"
+ },
+ "no-self-use": {
+ "enabled": true,
+ "colour": "lightcyan"
+ },
+ "use-a-generator": {
+ "enabled": true,
+ "colour": "yellow"
+ }
},
"properties": {
"long-element-chain": {
- "type": "boolean",
- "default": true,
- "description": "Long Element Chain"
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of long element chains."
+ },
+ "colour": {
+ "type": "string",
+ "default": "lightblue",
+ "description": "Colour (css syntax) for highlighting long element chains."
+ }
+ }
},
"too-many-arguments": {
- "type": "boolean",
- "default": true,
- "description": "Long Parameter List"
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of functions with too many arguments."
+ },
+ "colour": {
+ "type": "string",
+ "default": "lightcoral",
+ "description": "Colour (css syntax) for highlighting functions with too many arguments."
+ }
+ }
},
"long-lambda-expression": {
- "type": "boolean",
- "default": true,
- "description": "Long Lambda Expression"
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of long lambda expressions."
+ },
+ "colour": {
+ "type": "string",
+ "default": "mediumpurple",
+ "description": "Colour (css syntax) for highlighting long lambda expressions."
+ }
+ }
},
"long-message-chain": {
- "type": "boolean",
- "default": true,
- "description": "Long Message Chain"
- },
- "unused-variables-and-attributes": {
- "type": "boolean",
- "default": true,
- "description": "Unused Variables and Attributes"
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of long message chains."
+ },
+ "colour": {
+ "type": "string",
+ "default": "lightpink",
+ "description": "Colour (css syntax) for highlighting long message chains."
+ }
+ }
},
"cached-repeated-calls": {
- "type": "boolean",
- "default": true,
- "description": "Cached Repeated Calls"
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of cached repeated calls."
+ },
+ "colour": {
+ "type": "string",
+ "default": "lightgreen",
+ "description": "Colour (css syntax) for highlighting cached repeated calls."
+ }
+ }
},
"string-concat-loop": {
- "type": "boolean",
- "default": true,
- "description": "String Concatenation in Loop"
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of string concatenation in loops."
+ },
+ "colour": {
+ "type": "string",
+ "default": "lightsalmon",
+ "description": "Colour (css syntax) for highlighting string concatenation in loops."
+ }
+ }
},
"no-self-use": {
- "type": "boolean",
- "default": true,
- "description": "No Self Use"
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of methods with no self-use."
+ },
+ "colour": {
+ "type": "string",
+ "default": "lightcyan",
+ "description": "Colour (css syntax) for highlighting methods with no self-use."
+ }
+ }
},
"use-a-generator": {
- "type": "boolean",
- "default": true,
- "description": "Use a Generator"
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable detection of places where a generator could be used."
+ },
+ "colour": {
+ "type": "string",
+ "default": "yellow",
+ "description": "Colour (css syntax) for highlighting places where a generator could be used."
+ }
+ }
}
}
+ },
+ "ecooptimizer.detection.useSingleColour": {
+ "order": 2,
+ "type": "boolean",
+ "default": false,
+ "description": "Use a single colour for all smells. If enabled, the colour defined below will be used."
+ },
+ "ecooptimizer.detection.singleHighlightColour": {
+ "order": 3,
+ "type": "string",
+ "default": "yellow",
+ "markdownDescription": "Colour (css syntax) to use for all smells if **Use Single Colour** is enabled."
+ },
+ "ecooptimizer.detection.highlightStyle": {
+ "order": 0,
+ "type": "string",
+ "enum": [
+ "underline",
+ "flashlight",
+ "border-arrow"
+ ],
+ "markdownEnumDescriptions": [
+ "Your average wavy line",
+ "No pixel left untouched",
+ "Basically how it sounds"
+ ],
+ "default": "underline",
+ "description": "Choose a highlight style for all smells."
}
}
},
"keybindings": [
{
- "command": "ecooptimizer-vs-code-plugin.refactorSmell",
+ "command": "ecooptimizer.refactorSmell",
"key": "ctrl+shift+r",
"when": "editorTextFocus && resourceExtname == '.py'"
}
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 02f5223..f597f96 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -6,7 +6,8 @@ import { fetchSmells } from '../api/backend';
import { ContextManager } from '../context/contextManager';
import { envConfig } from '../utils/envConfig';
import { hashContent, updateHash } from '../utils/hashDocs';
-import { wipeWorkCache } from './wipeWorkCache'; // β
Import cache wipe function
+import { wipeWorkCache } from './wipeWorkCache';
+import { getEnabledSmells } from '../utils/handleSmellSettings';
import { serverStatus, ServerStatusType } from '../utils/serverStatus';
serverStatus.on('change', (newStatus: ServerStatusType) => {
@@ -118,7 +119,3 @@ export async function detectSmells(contextManager: ContextManager): Promise {
- const { editor, filePath } = getEditorAndFilePath();
-
- const pastData = contextManager.getWorkspaceData(
- envConfig.CURRENT_REFACTOR_DATA_KEY!,
- );
-
- // Clean up temp directory if not removed
- if (pastData) {
- cleanTemps(pastData);
- }
-
- if (!editor) {
- vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as no active editor found.',
- );
- console.log('No active editor found to refactor smell. Returning back.');
- return;
- }
- if (!filePath) {
- vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as active editor does not have a valid file path.',
- );
- console.log('No valid file path found to refactor smell. Returning back.');
- return;
- }
-
- // only account for one selection to be refactored for now
- // const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
-
- const smellsData: Smell[] = contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!,
- )[filePath].smells;
-
- if (!smellsData || smellsData.length === 0) {
- vscode.window.showErrorMessage(
- 'Eco: No smells detected in the file for refactoring.',
- );
- console.log('No smells found in the file for refactoring.');
- return;
- }
-
- // Filter smells by the given type ID
- const smellsOfType = smellsData.filter(
- (smell: Smell) => smell.messageId === smellId,
- );
-
- if (smellsOfType.length === 0) {
- vscode.window.showWarningMessage(
- `Eco: No smells of type ${smellId} found in the file.`,
- );
- return;
- }
-
- let combinedRefactoredData = '';
- let totalEnergySaved = 0;
- let allUpdatedSmells: Smell[] = [];
-
- // Refactor each smell of the given type
- for (const smell of smellsOfType) {
- const refactorResult = await refactorLine(smell, filePath);
-
- if (refactorResult && refactorResult.refactoredData) {
- // Add two newlines between each refactored result
- if (combinedRefactoredData) {
- combinedRefactoredData += '\n\n';
- }
-
- fs.readFile(
- refactorResult.refactoredData.targetFile.refactored,
- (err, data) => {
- if (!err) {
- combinedRefactoredData += data.toString('utf8');
- }
- },
- );
-
- totalEnergySaved += refactorResult.refactoredData.energySaved;
-
- if (refactorResult.updatedSmells) {
- allUpdatedSmells = [...allUpdatedSmells, ...refactorResult.updatedSmells];
- }
- }
- }
-
- /*
- Once all refactorings are merge, need to write to a file so that it has a path that
- will be the new `targetFile`. Also need to reconstruct the `RefactoredData` object
- by combining all `affectedFiles` merge to new paths if applicable. Once implemented,
- just uncomment lines below and pass in the refactoredData.
- */
-
- // Tentative data structure to be built below, change inputs as needed but needs
- // to implement the `MultiRefactoredData` interface
-
- // For any temp files that need to be written due to merging, I'd suggest writing them all
- // to one temp directory and add that directory to allTempDirs, that way they will be removed
-
- // UNCOMMENT ME WHEN READY
- // const combinedRefactoredData: MultiRefactoredData = {
- // targetFile: combinedTargetFile,
- // affectedFiles: allAffectedFiles,
- // energySaved: totalEnergySaved,
- // tempDirs: allTempDirs
+ // const { editor, filePath } = getEditorAndFilePath();
+ // const pastData = contextManager.getWorkspaceData(
+ // envConfig.CURRENT_REFACTOR_DATA_KEY!,
+ // );
+ // // Clean up temp directory if not removed
+ // if (pastData) {
+ // cleanTemps(pastData);
+ // }
+ // if (!editor) {
+ // vscode.window.showErrorMessage(
+ // 'Eco: Unable to proceed as no active editor found.',
+ // );
+ // console.log('No active editor found to refactor smell. Returning back.');
+ // return;
+ // }
+ // if (!filePath) {
+ // vscode.window.showErrorMessage(
+ // 'Eco: Unable to proceed as active editor does not have a valid file path.',
+ // );
+ // console.log('No valid file path found to refactor smell. Returning back.');
+ // return;
+ // }
+ // // only account for one selection to be refactored for now
+ // // const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
+ // const smellsData: Smell[] = contextManager.getWorkspaceData(
+ // envConfig.SMELL_MAP_KEY!,
+ // )[filePath].smells;
+ // if (!smellsData || smellsData.length === 0) {
+ // vscode.window.showErrorMessage(
+ // 'Eco: No smells detected in the file for refactoring.',
+ // );
+ // console.log('No smells found in the file for refactoring.');
+ // return;
+ // }
+ // // Filter smells by the given type ID
+ // const smellsOfType = smellsData.filter(
+ // (smell: Smell) => smell.messageId === smellId,
+ // );
+ // if (smellsOfType.length === 0) {
+ // vscode.window.showWarningMessage(
+ // `Eco: No smells of type ${smellId} found in the file.`,
+ // );
+ // return;
+ // }
+ // let combinedRefactoredData = '';
+ // let totalEnergySaved = 0;
+ // let allUpdatedSmells: Smell[] = [];
+ // // Refactor each smell of the given type
+ // for (const smell of smellsOfType) {
+ // const refactorResult = await refactorLine(smell, filePath);
+ // if (refactorResult && refactorResult.refactoredData) {
+ // // Add two newlines between each refactored result
+ // if (combinedRefactoredData) {
+ // combinedRefactoredData += '\n\n';
+ // }
+ // fs.readFile(
+ // refactorResult.refactoredData.targetFile.refactored,
+ // (err, data) => {
+ // if (!err) {
+ // combinedRefactoredData += data.toString('utf8');
+ // }
+ // },
+ // );
+ // totalEnergySaved += refactorResult.refactoredData.energySaved;
+ // if (refactorResult.updatedSmells) {
+ // allUpdatedSmells = [...allUpdatedSmells, ...refactorResult.updatedSmells];
+ // }
+ // }
+ // }
+ // /*
+ // Once all refactorings are merge, need to write to a file so that it has a path that
+ // will be the new `targetFile`. Also need to reconstruct the `RefactoredData` object
+ // by combining all `affectedFiles` merge to new paths if applicable. Once implemented,
+ // just uncomment lines below and pass in the refactoredData.
+ // */
+ // // Tentative data structure to be built below, change inputs as needed but needs
+ // // to implement the `MultiRefactoredData` interface
+ // // For any temp files that need to be written due to merging, I'd suggest writing them all
+ // // to one temp directory and add that directory to allTempDirs, that way they will be removed
+ // // UNCOMMENT ME WHEN READY
+ // // const combinedRefactoredData: MultiRefactoredData = {
+ // // targetFile: combinedTargetFile,
+ // // affectedFiles: allAffectedFiles,
+ // // energySaved: totalEnergySaved,
+ // // tempDirs: allTempDirs
+ // // }
+ // // UNCOMMENT ME WHEN READY
+ // // startRefactoringSession(contextManager,editor,combinedRefactoredData);
+ // if (combinedRefactoredData) {
+ // // await RefactorManager.previewRefactor(editor, combinedRefactoredData);
+ // vscode.window.showInformationMessage(
+ // `Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(
+ // 4,
+ // )}`,
+ // );
+ // } else {
+ // vscode.window.showErrorMessage(
+ // 'Eco: Refactoring failed. See console for details.',
+ // );
+ // return;
+ // }
+ // if (allUpdatedSmells.length) {
+ // const fileHighlighter = FileHighlighter.getInstance(contextManager);
+ // fileHighlighter.highlightSmells(editor, allUpdatedSmells);
+ // } else {
+ // vscode.window.showWarningMessage(
+ // 'Eco: No updated smells detected after refactoring.',
+ // );
// }
-
- // UNCOMMENT ME WHEN READY
- // startRefactoringSession(contextManager,editor,combinedRefactoredData);
-
- if (combinedRefactoredData) {
- // await RefactorManager.previewRefactor(editor, combinedRefactoredData);
- vscode.window.showInformationMessage(
- `Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(
- 4,
- )}`,
- );
- } else {
- vscode.window.showErrorMessage(
- 'Eco: Refactoring failed. See console for details.',
- );
- return;
- }
-
- if (allUpdatedSmells.length) {
- const fileHighlighter = FileHighlighter.getInstance(contextManager);
- fileHighlighter.highlightSmells(editor, allUpdatedSmells);
- } else {
- vscode.window.showWarningMessage(
- 'Eco: No updated smells detected after refactoring.',
- );
- }
}
/* istanbul ignore next */
@@ -324,7 +304,7 @@ async function startRefactoringSession(
refactoredCode,
'Refactoring Comparison',
);
- vscode.commands.executeCommand('ecooptimizer-vs-code-plugin.showRefactorSidebar');
+ vscode.commands.executeCommand('ecooptimizer.showRefactorSidebar');
sidebarState.isOpening = false;
}
diff --git a/src/extension.ts b/src/extension.ts
index 0972792..10fc13a 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -61,34 +61,28 @@ export function activate(context: vscode.ExtensionContext): void {
// Detect Smells Command
context.subscriptions.push(
- vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.detectSmells',
- async () => {
- console.log('Eco: Detect Smells Command Triggered');
- detectSmells(contextManager);
- },
- ),
+ vscode.commands.registerCommand('ecooptimizer.detectSmells', async () => {
+ console.log('Eco: Detect Smells Command Triggered');
+ detectSmells(contextManager);
+ }),
);
// Refactor Selected Smell Command
context.subscriptions.push(
- vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.refactorSmell',
- () => {
- if (serverStatus.getStatus() === 'up') {
- console.log('Eco: Refactor Selected Smell Command Triggered');
- refactorSelectedSmell(contextManager);
- } else {
- vscode.window.showWarningMessage('Action blocked: Server is down.');
- }
- },
- ),
+ vscode.commands.registerCommand('ecooptimizer.refactorSmell', () => {
+ if (serverStatus.getStatus() === 'up') {
+ console.log('Eco: Refactor Selected Smell Command Triggered');
+ refactorSelectedSmell(contextManager);
+ } else {
+ vscode.window.showWarningMessage('Action blocked: Server is down.');
+ }
+ }),
);
// Refactor All Smells of Type Command
context.subscriptions.push(
vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.refactorAllSmellsOfType',
+ 'ecooptimizer.refactorAllSmellsOfType',
async (smellId: string) => {
if (serverStatus.getStatus() === 'up') {
console.log(
@@ -104,21 +98,18 @@ export function activate(context: vscode.ExtensionContext): void {
// Wipe Cache Command
context.subscriptions.push(
- vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.wipeWorkCache',
- async () => {
- console.log('Eco: Wipe Work Cache Command Triggered');
- vscode.window.showInformationMessage(
- 'Eco: Manually wiping workspace memory... β
',
- );
- await wipeWorkCache(contextManager, 'manual');
- },
- ),
+ vscode.commands.registerCommand('ecooptimizer.wipeWorkCache', async () => {
+ console.log('Eco: Wipe Work Cache Command Triggered');
+ vscode.window.showInformationMessage(
+ 'Eco: Manually wiping workspace memory... β
',
+ );
+ await wipeWorkCache(contextManager, 'manual');
+ }),
);
// screen button go brr
context.subscriptions.push(
- vscode.commands.registerCommand('eco.toggleSmellLinting', () => {
+ vscode.commands.registerCommand('ecooptimizer.toggleSmellLinting', () => {
console.log('Eco: Toggle Smell Linting Command Triggered');
toggleSmellLinting(contextManager);
}),
@@ -137,23 +128,20 @@ export function activate(context: vscode.ExtensionContext): void {
);
context.subscriptions.push(
- vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.showRefactorSidebar',
- () => refactorProvider.updateView(),
+ vscode.commands.registerCommand('ecooptimizer.showRefactorSidebar', () =>
+ refactorProvider.updateView(),
),
);
context.subscriptions.push(
- vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.pauseRefactorSidebar',
- () => refactorProvider.pauseView(),
+ vscode.commands.registerCommand('ecooptimizer.pauseRefactorSidebar', () =>
+ refactorProvider.pauseView(),
),
);
context.subscriptions.push(
- vscode.commands.registerCommand(
- 'ecooptimizer-vs-code-plugin.clearRefactorSidebar',
- () => refactorProvider.clearView(),
+ vscode.commands.registerCommand('ecooptimizer.clearRefactorSidebar', () =>
+ refactorProvider.clearView(),
),
);
@@ -261,7 +249,7 @@ export function activate(context: vscode.ExtensionContext): void {
function showSettingsPopup(): void {
// Check if the required settings are already configured
- const config = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin');
+ const config = vscode.workspace.getConfiguration('ecooptimizer');
const workspacePath = config.get('projectWorkspacePath', '');
const logsOutputPath = config.get('logsOutputPath', '');
const unitTestPath = config.get('unitTestPath', '');
@@ -301,9 +289,9 @@ function showSettingsPopup(): void {
function handleConfigurationChange(event: vscode.ConfigurationChangeEvent): void {
// Check if any relevant setting was changed
if (
- event.affectsConfiguration('ecooptimizer-vs-code-plugin.projectWorkspacePath') ||
- event.affectsConfiguration('ecooptimizer-vs-code-plugin.unitTestCommand') ||
- event.affectsConfiguration('ecooptimizer-vs-code-plugin.logsOutputPath')
+ event.affectsConfiguration('ecooptimizer.projectWorkspacePath') ||
+ event.affectsConfiguration('ecooptimizer.unitTestCommand') ||
+ event.affectsConfiguration('ecooptimizer.logsOutputPath')
) {
// Display a warning message about changing critical settings
vscode.window.showWarningMessage(
diff --git a/src/global.d.ts b/src/global.d.ts
index 72c878a..cf5f436 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -60,7 +60,6 @@ declare global {
export type SmellDetails = {
symbol: string;
message: string;
- colour: string; // RGB colour as a string
};
}
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index 09cdf55..1d95989 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -2,7 +2,6 @@ import * as vscode from 'vscode';
import { getEditor } from '../utils/editorUtils';
import { ContextManager } from '../context/contextManager';
import { HoverManager } from './hoverManager';
-import { SMELL_MAP } from '../utils/smellDetails';
export class FileHighlighter {
private static instance: FileHighlighter;
@@ -21,76 +20,100 @@ export class FileHighlighter {
}
public resetHighlights(): void {
- if (this.decorations.length > 0) {
- console.log('Removing decorations');
- this.decorations.forEach((decoration) => {
- decoration.dispose();
- });
- this.decorations = []; // Clear the decorations array
- }
+ this.decorations.forEach((decoration) => decoration.dispose());
+ this.decorations = [];
}
public highlightSmells(editor: vscode.TextEditor, smells: Smell[]): void {
this.resetHighlights();
- const activeSmells = new Set(smells.map((smell) => smell.messageId));
+ const config = vscode.workspace.getConfiguration('ecooptimizer.detection');
+ const smellsConfig = config.get<{
+ [key: string]: { enabled: boolean; colour: string };
+ }>('smells', {});
+ const useSingleColour = config.get('useSingleColour', false);
+ const singleHighlightColour = config.get(
+ 'singleHighlightColour',
+ 'rgba(255, 204, 0, 0.5)',
+ );
+ const highlightStyle = config.get('highlightStyle', 'underline');
+
+ const activeSmells = new Set(smells.map((smell) => smell.symbol));
activeSmells.forEach((smellType) => {
- this.highlightSmell(editor, smells, smellType);
+ const smellConfig = smellsConfig[smellType];
+ if (smellConfig?.enabled) {
+ const colour = useSingleColour ? singleHighlightColour : smellConfig.colour;
+ this.highlightSmell(editor, smells, smellType, colour, highlightStyle);
+ }
});
-
- console.log('Updated smell line highlights');
}
- public highlightSmell(
+ private highlightSmell(
editor: vscode.TextEditor,
smells: Smell[],
targetSmell: string,
+ colour: string,
+ style: string,
): void {
const smellLines: vscode.DecorationOptions[] = smells
.filter((smell: Smell) => {
const valid = smell.occurences.every((occurrence: { line: number }) =>
isValidLine(occurrence.line),
);
- const isCorrectType = smell.messageId === targetSmell;
+ const isCorrectType = smell.symbol === targetSmell;
return valid && isCorrectType;
})
.map((smell: Smell) => {
const line = smell.occurences[0].line - 1; // convert to zero-based line index for VS editor
-
- const line_text = editor.document.lineAt(line).text;
- const line_length = line_text.length;
- const indexStart = line_length - line_text.trimStart().length;
- const indexEnd = line_text.trimEnd().length + 2;
-
+ const lineText = editor.document.lineAt(line).text;
+ const indexStart = lineText.length - lineText.trimStart().length;
+ const indexEnd = lineText.trimEnd().length + 2;
const range = new vscode.Range(line, indexStart, line, indexEnd);
const hoverManager = HoverManager.getInstance(this.contextManager, smells);
- return { range, hoverMessage: hoverManager.hoverContent || undefined }; // option to hover over and read smell details
+ return { range, hoverMessage: hoverManager.hoverContent || undefined };
});
- const colorOfSmell = SMELL_MAP.get(targetSmell)!.colour;
-
- editor.setDecorations(this.getDecoration(colorOfSmell), smellLines);
+ console.log('Highlighting smell:', targetSmell, colour, style, smellLines);
+ const decoration = this.getDecoration(colour, style);
+ editor.setDecorations(decoration, smellLines);
+ this.decorations.push(decoration);
}
- private getDecoration(color: string): vscode.TextEditorDecorationType {
- const aLittleExtra = vscode.window.createTextEditorDecorationType({
- borderWidth: '1px 2px 1px 0', // Top, Right, Bottom, No Left border
- borderStyle: 'solid',
- borderColor: color, // Change as needed
- after: {
- contentText: 'βΆ', // Unicode right arrow
- margin: '0 0 0 5px', // Space between line and arrow
- color: color,
- fontWeight: 'bold',
- },
- overviewRulerColor: color,
- overviewRulerLane: vscode.OverviewRulerLane.Right,
- });
-
- this.decorations.push(aLittleExtra); // Add the decoration to the list
- return aLittleExtra;
+ private getDecoration(
+ colour: string,
+ style: string,
+ ): vscode.TextEditorDecorationType {
+ switch (style) {
+ case 'underline':
+ return vscode.window.createTextEditorDecorationType({
+ textDecoration: `wavy ${colour} underline 1px`,
+ });
+ case 'flashlight':
+ return vscode.window.createTextEditorDecorationType({
+ isWholeLine: true,
+ backgroundColor: colour,
+ });
+ case 'border-arrow':
+ return vscode.window.createTextEditorDecorationType({
+ borderWidth: '1px 2px 1px 0',
+ borderStyle: 'solid',
+ borderColor: colour,
+ after: {
+ contentText: 'βΆ',
+ margin: '0 0 0 5px',
+ color: colour,
+ fontWeight: 'bold',
+ },
+ overviewRulerColor: colour,
+ overviewRulerLane: vscode.OverviewRulerLane.Right,
+ });
+ default:
+ return vscode.window.createTextEditorDecorationType({
+ textDecoration: `wavy ${colour} underline 1px`,
+ });
+ }
}
}
diff --git a/src/utils/configManager.ts b/src/utils/configManager.ts
index 3874d3a..45e49f5 100644
--- a/src/utils/configManager.ts
+++ b/src/utils/configManager.ts
@@ -10,7 +10,7 @@ export class ConfigManager {
// get workspace path
static getWorkspacePath(): string {
const rawPath = vscode.workspace
- .getConfiguration('ecooptimizer-vs-code-plugin')
+ .getConfiguration('ecooptimizer')
.get('projectWorkspacePath', '');
const resolvedPath =
rawPath || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
@@ -24,7 +24,7 @@ export class ConfigManager {
// get logs output path
static getLogsOutputPath(): string {
const rawPath = vscode.workspace
- .getConfiguration('ecooptimizer-vs-code-plugin')
+ .getConfiguration('ecooptimizer')
.get('logsOutputPath', '');
const workspacePath = this.getWorkspacePath();
const resolvedPath = rawPath || `${workspacePath}/logs`;
@@ -39,10 +39,8 @@ export class ConfigManager {
static onConfigChange(callback: () => void): void {
vscode.workspace.onDidChangeConfiguration((event) => {
if (
- event.affectsConfiguration(
- 'ecooptimizer-vs-code-plugin.projectWorkspacePath',
- ) ||
- event.affectsConfiguration('ecooptimizer-vs-code-plugin.logsOutputPath')
+ event.affectsConfiguration('ecooptimizer.projectWorkspacePath') ||
+ event.affectsConfiguration('ecooptimizer.logsOutputPath')
) {
callback();
}
@@ -51,7 +49,7 @@ export class ConfigManager {
// write settings to both User and Workspace if necessary
private static writeSetting(setting: string, value: string): void {
- const config = vscode.workspace.getConfiguration('ecooptimizer-vs-code-plugin');
+ const config = vscode.workspace.getConfiguration('ecooptimizer');
// inspect current values in both User and Workspace settings
const currentValueGlobal = config.inspect(setting)?.globalValue;
diff --git a/src/utils/handleEditorChange.ts b/src/utils/handleEditorChange.ts
index fd70431..123745c 100644
--- a/src/utils/handleEditorChange.ts
+++ b/src/utils/handleEditorChange.ts
@@ -56,7 +56,7 @@ export async function handleEditorChanges(
contextManager.setWorkspaceData(envConfig.ACTIVE_DIFF_KEY!, diffState);
await setTimeout(500);
// vscode.commands.executeCommand(
- // 'ecooptimizer-vs-code-plugin.pauseRefactorSidebar'
+ // 'ecooptimizer.pauseRefactorSidebar'
// );
return;
}
@@ -78,9 +78,7 @@ export async function handleEditorChanges(
// console.log(`diffstate: ${JSON.stringify(diffState)}`);
contextManager.setWorkspaceData(envConfig.ACTIVE_DIFF_KEY!, diffState);
await setTimeout(500);
- vscode.commands.executeCommand(
- 'ecooptimizer-vs-code-plugin.showRefactorSidebar',
- );
+ vscode.commands.executeCommand('ecooptimizer.showRefactorSidebar');
}
console.log('Doing nothing');
}
diff --git a/src/utils/handleSmellSettings.ts b/src/utils/handleSmellSettings.ts
index f36100a..2fad66b 100644
--- a/src/utils/handleSmellSettings.ts
+++ b/src/utils/handleSmellSettings.ts
@@ -5,8 +5,16 @@ import { ContextManager } from '../context/contextManager';
/**
* Fetches the current enabled smells from VS Code settings.
*/
-export function getEnabledSmells(): { [key: string]: boolean } {
- return vscode.workspace.getConfiguration('ecooptimizer').get('enableSmells', {});
+export function getEnabledSmells(): {
+ [key: string]: boolean;
+} {
+ const smellConfig = vscode.workspace
+ .getConfiguration('detection')
+ .get('smells', {}) as { [key: string]: { enabled: boolean; colour: string } };
+
+ return Object.fromEntries(
+ Object.entries(smellConfig).map(([smell, config]) => [smell, config.enabled]),
+ );
}
/**
@@ -31,7 +39,6 @@ export function handleSmellFilterUpdate(
}
});
- // If any smell preference changed, wipe the cache
if (smellsChanged) {
console.log('Eco: Smell preferences changed! Wiping cache.');
wipeWorkCache(contextManager, 'settings');
@@ -42,7 +49,5 @@ export function handleSmellFilterUpdate(
* Formats the smell name from kebab-case to a readable format.
*/
export function formatSmellName(smellKey: string): string {
- return smellKey
- .replace(/-/g, ' ') // Replace hyphens with spaces
- .replace(/\b\w/g, (char) => char.toUpperCase()); // Capitalize first letter
+ return smellKey.replace(/-/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase());
}
diff --git a/src/utils/smellDetails.ts b/src/utils/smellDetails.ts
index d6b46d5..bcc2b8c 100644
--- a/src/utils/smellDetails.ts
+++ b/src/utils/smellDetails.ts
@@ -1,83 +1,74 @@
export const SMELL_MAP: Map = new Map([
[
- 'R1729', // id: "use-a-generator"
+ 'R1729',
{
symbol: 'use-a-generator',
message:
'Refactor to use a generator expression instead of a list comprehension inside `any()` or `all()`. This improves memory efficiency by avoiding the creation of an intermediate list.',
- colour: 'rgb(255, 204, 0)', // Yellow
},
],
[
- 'R0913', // id: "too-many-arguments"
+ 'R0913',
{
symbol: 'too-many-arguments',
message:
'Refactor the function to reduce the number of parameters. Functions with too many arguments can become difficult to maintain and understand. Consider breaking it into smaller, more manageable functions.',
- colour: 'rgb(255, 102, 102)', // Light Red
},
],
[
- 'R6301', // id: "no-self-use"
+ 'R6301',
{
symbol: 'no-self-use',
message:
"Refactor the method to make it static, as it does not use `self`. Static methods do not require an instance and improve clarity and performance when the method doesn't depend on instance data.",
- colour: 'rgb(204, 255, 255)', // Light Cyan
},
],
[
- 'LLE001', // id: "long-lambda-expression"
+ 'LLE001',
{
symbol: 'long-lambda-expression',
message:
'Refactor the lambda expression to improve readability. Long lambda expressions can be confusing; breaking them into named functions can make the code more understandable and maintainable.',
- colour: 'rgb(153, 102, 255)', // Light Purple
},
],
[
- 'LMC001', // id: "long-message-chain"
+ 'LMC001',
{
symbol: 'long-message-chain',
message:
'Refactor the message chain to improve readability and performance. Long chains of method calls can be hard to follow and may impact performance. Consider breaking them into smaller steps.',
- colour: 'rgb(255, 204, 255)', // Light Pink
},
],
[
- 'UVA001', // id: "unused_variables_and_attributes"
+ 'UVA001',
{
symbol: 'unused-variables-and-attributes',
message:
'Remove unused variables or attributes to clean up the code. Keeping unused elements in the code increases its complexity without providing any benefit, making it harder to maintain.',
- colour: 'rgb(255, 255, 102)', // Light Yellow
},
],
[
- 'LEC001', // id: "long-element-chain"
+ 'LEC001',
{
symbol: 'long-element-chain',
message:
'Refactor the long element chain for better performance and clarity. Chains of nested elements are harder to read and can lead to inefficiency, especially when accessing deep levels repeatedly.',
- colour: 'rgb(204, 204, 255)', // Light Blue
},
],
[
- 'CRC001', // id: "cached-repeated-calls"
+ 'CRC001',
{
symbol: 'cached-repeated-calls',
message:
'Refactor by caching repeated function calls to improve performance. Repeated calls to the same function can be avoided by storing the result, which saves processing time and enhances performance.',
- colour: 'rgb(102, 255, 102)', // Light Green
},
],
[
- 'SCL001', // id: "string-concat-loop"
+ 'SCL001',
{
symbol: 'string-concat-loop',
message:
'Refactor to use list accumulation instead of string concatenation inside a loop. Concatenating strings in a loop is inefficient; list accumulation and joining are faster and use less memory.',
- colour: 'rgb(255, 178, 102)', // Light Orange
},
],
]);
diff --git a/test/commands/detectSmells.test.ts b/test/commands/detectSmells.test.ts
index 3e9e3d9..4767c45 100644
--- a/test/commands/detectSmells.test.ts
+++ b/test/commands/detectSmells.test.ts
@@ -2,11 +2,12 @@
import { ContextManager } from '../../src/context/contextManager';
import { FileHighlighter } from '../../src/ui/fileHighlighter';
-import vscode, { config } from '../mocks/vscode-mock';
+import vscode from '../mocks/vscode-mock';
import * as backend from '../../src/api/backend';
import * as hashDocs from '../../src/utils/hashDocs';
import * as editorUtils from '../../src/utils/editorUtils';
+import * as SmellSettings from '../../src/utils/handleSmellSettings';
import { detectSmells } from '../../src/commands/detectSmells';
import { serverStatus, ServerStatusType } from '../../src/utils/serverStatus';
@@ -17,6 +18,13 @@ jest.mock('../../src/commands/wipeWorkCache', () => ({
wipeWorkCache: jest.fn(),
}));
+jest.mock('../../src/utils/handleSmellSettings.ts', () => ({
+ getEnabledSmells: jest.fn().mockImplementation(() => ({
+ smell1: true,
+ smell2: true,
+ })),
+}));
+
describe('detectSmells', () => {
let contextManagerMock: ContextManager;
@@ -64,9 +72,9 @@ describe('detectSmells', () => {
filePath: 'fake.path',
});
- jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
- get: jest.fn().mockReturnValue({ smell1: false, smell2: false }),
- });
+ jest
+ .spyOn(SmellSettings, 'getEnabledSmells')
+ .mockReturnValueOnce({ smell1: false, smell2: false });
await detectSmells(contextManagerMock);
@@ -86,7 +94,7 @@ describe('detectSmells', () => {
jest
.spyOn(contextManagerMock, 'getWorkspaceData')
- .mockReturnValueOnce(config.configGet)
+ .mockReturnValueOnce({ smell1: true, smell2: true })
.mockReturnValueOnce({
'fake.path': {
hash: 'someHash',
@@ -122,9 +130,6 @@ describe('detectSmells', () => {
jest.spyOn(serverStatus, 'getStatus').mockReturnValue(ServerStatusType.UP);
- // Simulate no smells enabled
- // config.configGet = { smell1: false, smell2: false };
-
await detectSmells(contextManagerMock);
expect(wipeWorkCache).toHaveBeenCalled();
@@ -133,7 +138,7 @@ describe('detectSmells', () => {
expect(contextManagerMock.setWorkspaceData).toHaveBeenCalledTimes(2);
});
- it('should fetch new smells hash change, same enabled smells', async () => {
+ it('should fetch new smells on hash change, same enabled smells', async () => {
jest.spyOn(editorUtils, 'getEditorAndFilePath').mockReturnValueOnce({
editor: vscode.window.activeTextEditor,
filePath: 'fake.path',
@@ -144,7 +149,7 @@ describe('detectSmells', () => {
jest
.spyOn(contextManagerMock, 'getWorkspaceData')
- .mockReturnValueOnce(config.configGet)
+ .mockReturnValueOnce({ smell1: true, smell2: true })
.mockReturnValueOnce({
'fake.path': {
hash: 'differentHash',
@@ -156,9 +161,6 @@ describe('detectSmells', () => {
jest.spyOn(backend, 'fetchSmells').mockResolvedValueOnce([]);
- // Simulate no smells enabled
- // config.configGet = { smell1: false, smell2: false };
-
await detectSmells(contextManagerMock);
expect(hashDocs.updateHash).toHaveBeenCalled();
@@ -177,7 +179,7 @@ describe('detectSmells', () => {
jest
.spyOn(contextManagerMock, 'getWorkspaceData')
- .mockReturnValueOnce(config.configGet)
+ .mockReturnValueOnce({ smell1: true, smell2: true })
.mockReturnValueOnce({});
jest.spyOn(serverStatus, 'getStatus').mockReturnValue(ServerStatusType.DOWN);
@@ -199,7 +201,7 @@ describe('detectSmells', () => {
jest
.spyOn(contextManagerMock, 'getWorkspaceData')
- .mockReturnValueOnce(config.configGet)
+ .mockReturnValueOnce({ smell1: true, smell2: true })
.mockReturnValueOnce({
'fake.path': {
hash: 'someHash',
diff --git a/test/mocks/vscode-mock.ts b/test/mocks/vscode-mock.ts
index 7b36a8f..e058729 100644
--- a/test/mocks/vscode-mock.ts
+++ b/test/mocks/vscode-mock.ts
@@ -80,10 +80,7 @@ interface Workspace {
export const workspace: Workspace = {
getConfiguration: jest.fn((section?: string) => ({
- get: jest.fn((key: string, _defaultReturn: any) => {
- console.log(`MOCK getConfiguration: ${section}.${key}`);
- return config.configGet;
- }),
+ get: jest.fn(),
})),
};
diff --git a/test/ui/fileHighlighter.test.ts b/test/ui/fileHighlighter.test.ts
index 74eaf20..db45873 100644
--- a/test/ui/fileHighlighter.test.ts
+++ b/test/ui/fileHighlighter.test.ts
@@ -26,10 +26,20 @@ describe('File Highlighter', () => {
it('should not reset highlight decorations on first init', () => {
const smells = [
{
- messageId: 'R1729',
+ symbol: 'smell1',
occurences: [{ line: 1 }],
},
] as unknown as Smell[];
+ const currentConfig = {
+ smell1: {
+ enabled: true,
+ colour: 'rgba(1, 50, 0, 0.5)',
+ },
+ };
+
+ jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
+ get: jest.fn().mockReturnValue(currentConfig),
+ } as any);
jest.spyOn(HoverManager, 'getInstance').mockReturnValueOnce({
hoverContent: 'hover content' as unknown as MarkdownString,
@@ -43,7 +53,7 @@ describe('File Highlighter', () => {
it('should create decorations', () => {
const color = 'red';
- const decoration = fileHighlighter['getDecoration'](color);
+ const decoration = fileHighlighter['getDecoration'](color, 'underline');
// Assert decoration was created
expect(vscode.window.createTextEditorDecorationType).toHaveBeenCalled();
@@ -53,30 +63,52 @@ describe('File Highlighter', () => {
it('should highlight smells', () => {
const smells = [
{
- messageId: 'smell1',
+ symbol: 'smell1',
occurences: [{ line: 1 }],
},
] as unknown as Smell[];
+ const currentConfig = {
+ smell1: {
+ enabled: true,
+ colour: 'rgba(88, 101, 200, 0.5)',
+ },
+ };
+
+ jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
+ get: jest.fn().mockReturnValue(currentConfig),
+ } as any);
jest.spyOn(HoverManager, 'getInstance').mockReturnValueOnce({
hoverContent: 'hover content' as unknown as MarkdownString,
} as unknown as HoverManager);
- fileHighlighter.highlightSmell(vscode.window.activeTextEditor, smells, 'R1729');
+ fileHighlighter.highlightSmells(vscode.window.activeTextEditor, smells);
- // Assert decorations were set
expect(vscode.window.activeTextEditor.setDecorations).toHaveBeenCalled();
+ expect(
+ vscode.window.activeTextEditor.setDecorations.mock.calls[0][1],
+ ).toHaveLength(1);
});
it('should reset highlight decorations on subsequent calls', () => {
const smells = [
{
- messageId: 'R1729',
+ symbol: 'smell1',
occurences: [{ line: 1 }],
},
] as unknown as Smell[];
+ const currentConfig = {
+ smell1: {
+ enabled: true,
+ colour: 'rgba(255, 204, 0, 0.5)',
+ },
+ };
- jest.spyOn(HoverManager, 'getInstance').mockReturnValueOnce({
+ jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValue({
+ get: jest.fn().mockReturnValue(currentConfig),
+ } as any);
+
+ jest.spyOn(HoverManager, 'getInstance').mockReturnValue({
hoverContent: 'hover content' as unknown as MarkdownString,
} as unknown as HoverManager);
diff --git a/test/utils/handleSmellSettings.test.ts b/test/utils/handleSmellSettings.test.ts
index 6ea155c..0598534 100644
--- a/test/utils/handleSmellSettings.test.ts
+++ b/test/utils/handleSmellSettings.test.ts
@@ -1,4 +1,3 @@
-import * as vscode from 'vscode';
import {
handleSmellFilterUpdate,
getEnabledSmells,
@@ -6,7 +5,7 @@ import {
} from '../../src/utils/handleSmellSettings';
import { wipeWorkCache } from '../../src/commands/wipeWorkCache';
import { ContextManager } from '../../src/context/contextManager';
-import vscodeMock from '../mocks/vscode-mock';
+import vscode from '../mocks/vscode-mock';
jest.mock('../../src/commands/wipeWorkCache', () => ({
wipeWorkCache: jest.fn(),
@@ -25,18 +24,27 @@ describe('Settings Page - handleSmellSettings.ts', () => {
describe('getEnabledSmells', () => {
it('should return the current enabled smells from settings', () => {
- const mockSmells = {
- 'cached-repeated-calls': true,
- 'long-element-chain': false,
+ const currentConfig = {
+ 'cached-repeated-calls': {
+ enabled: true,
+ colour: 'rgba(255, 204, 0, 0.5)',
+ },
+ 'long-element-chain': {
+ enabled: false,
+ colour: 'rgba(255, 204, 0, 0.5)',
+ },
};
jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
- get: jest.fn().mockReturnValue(mockSmells),
+ get: jest.fn().mockReturnValue(currentConfig),
} as any);
const enabledSmells = getEnabledSmells();
- expect(enabledSmells).toEqual(mockSmells);
+ expect(enabledSmells).toEqual({
+ 'cached-repeated-calls': true,
+ 'long-element-chain': false,
+ });
});
it('should return an empty object if no smells are set', () => {
@@ -52,10 +60,15 @@ describe('Settings Page - handleSmellSettings.ts', () => {
describe('handleSmellFilterUpdate', () => {
it('should detect when a smell is enabled and notify the user', () => {
const previousSmells = { 'cached-repeated-calls': false };
- const currentSmells = { 'cached-repeated-calls': true };
+ const currentConfig = {
+ 'cached-repeated-calls': {
+ enabled: true,
+ colour: 'rgba(255, 204, 0, 0.5)',
+ },
+ };
jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
- get: jest.fn().mockReturnValue(currentSmells),
+ get: jest.fn().mockReturnValue(currentConfig),
} as any);
handleSmellFilterUpdate(previousSmells, contextManagerMock);
@@ -68,10 +81,15 @@ describe('Settings Page - handleSmellSettings.ts', () => {
it('should detect when a smell is disabled and notify the user', () => {
const previousSmells = { 'long-element-chain': true };
- const currentSmells = { 'long-element-chain': false };
+ const currentConfig = {
+ 'long-element-chain': {
+ enabled: false,
+ colour: 'rgba(255, 204, 0, 0.5)',
+ },
+ };
jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
- get: jest.fn().mockReturnValue(currentSmells),
+ get: jest.fn().mockReturnValue(currentConfig),
} as any);
handleSmellFilterUpdate(previousSmells, contextManagerMock);
@@ -84,10 +102,15 @@ describe('Settings Page - handleSmellSettings.ts', () => {
it('should not wipe cache if no smells changed', () => {
const previousSmells = { 'cached-repeated-calls': true };
- const currentSmells = { 'cached-repeated-calls': true };
+ const currentConfig = {
+ 'cached-repeated-calls': {
+ enabled: true,
+ colour: 'rgba(255, 204, 0, 0.5)',
+ },
+ };
jest.spyOn(vscode.workspace, 'getConfiguration').mockReturnValueOnce({
- get: jest.fn().mockReturnValue(currentSmells),
+ get: jest.fn().mockReturnValue(currentConfig),
} as any);
handleSmellFilterUpdate(previousSmells, contextManagerMock);
From 82cda06f8cb907c9b6045c7cb139320e67c76c2a Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Tue, 18 Mar 2025 11:44:04 -0400
Subject: [PATCH 80/83] Updated log message processing (#9)
closes ssm-lab/capstone--source-code-optimizer#517
---
src/commands/showLogs.ts | 128 +++++++++++++++++++++++++--------------
1 file changed, 84 insertions(+), 44 deletions(-)
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index efc7ce6..38229d3 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -22,20 +22,37 @@ class WebSocketInitializationError extends Error {
const WEBSOCKET_BASE_URL = `ws://${envConfig.SERVER_URL}/logs`;
-let websockets: WebSocket[] = [];
-
-let mainLogChannel: vscode.OutputChannel | undefined;
-let detectSmellsChannel: vscode.OutputChannel | undefined;
-let refactorSmellChannel: vscode.OutputChannel | undefined;
+let websockets: { [key: string]: WebSocket | undefined } = {
+ main: undefined,
+ detect: undefined,
+ refactor: undefined,
+};
+
+let channels: {
+ [key: string]: { name: string; channel: vscode.LogOutputChannel | undefined };
+} = {
+ main: {
+ name: 'EcoOptimizer: Main',
+ channel: undefined,
+ },
+ detect: {
+ name: 'EcoOptimizer: Detect',
+ channel: undefined,
+ },
+ refactor: {
+ name: 'EcoOptimizer: Refactor',
+ channel: undefined,
+ },
+};
let CHANNELS_CREATED = false;
serverStatus.on('change', async (newStatus: ServerStatusType) => {
console.log('Server status changed:', newStatus);
if (newStatus === ServerStatusType.DOWN) {
- mainLogChannel?.appendLine('Server connection lost');
+ channels.main.channel?.appendLine('Server connection lost');
} else {
- mainLogChannel?.appendLine('Server connection re-established.');
+ channels.main.channel?.appendLine('Server connection re-established.');
await startLogging();
}
});
@@ -43,6 +60,7 @@ serverStatus.on('change', async (newStatus: ServerStatusType) => {
export async function startLogging(retries = 3, delay = 1000): Promise {
let logInitialized = false;
const logPath = globalData.contextManager?.context.logUri?.fsPath;
+ console.log('log path:', logPath);
if (!logPath) {
console.error('Missing contextManager or logUri. Cannot initialize logging.');
@@ -62,18 +80,10 @@ export async function startLogging(retries = 3, delay = 1000): Promise {
console.log('Log initialization successful.');
}
- if (CHANNELS_CREATED) {
- console.warn(
- 'Logging channels already initialized. Skipping WebSocket setup.',
- );
- return;
- }
-
- // Try initializing WebSockets separately
try {
initializeWebSockets();
console.log('Successfully initialized WebSockets. Logging is now active.');
- return; // Exit function if everything is successful
+ return;
} catch {
throw new WebSocketInitializationError('Failed to initialize WebSockets.');
}
@@ -93,46 +103,78 @@ export async function startLogging(retries = 3, delay = 1000): Promise {
}
function initializeWebSockets(): void {
- startWebSocket('main', 'EcoOptimizer: Main Logs');
- startWebSocket('detect', 'EcoOptimizer: Detect Smells');
- startWebSocket('refactor', 'EcoOptimizer: Refactor Smell');
+ if (!CHANNELS_CREATED) {
+ createOutputChannels();
+ CHANNELS_CREATED = true;
+ }
+ startWebSocket('main');
+ startWebSocket('detect');
+ startWebSocket('refactor');
+}
- CHANNELS_CREATED = true;
+function createOutputChannels(): void {
+ console.log('Creating ouput channels');
+ for (const channel of Object.keys(channels)) {
+ channels[channel].channel = vscode.window.createOutputChannel(
+ channels[channel].name,
+ { log: true },
+ );
+ }
}
-function startWebSocket(logType: string, channelName: string): void {
+function startWebSocket(logType: string): void {
const url = `${WEBSOCKET_BASE_URL}/${logType}`;
const ws = new WebSocket(url);
- websockets.push(ws);
-
- let channel: vscode.OutputChannel;
- if (logType === 'main') {
- mainLogChannel = vscode.window.createOutputChannel(channelName);
- channel = mainLogChannel;
- } else if (logType === 'detect') {
- detectSmellsChannel = vscode.window.createOutputChannel(channelName);
- channel = detectSmellsChannel;
- } else if (logType === 'refactor') {
- refactorSmellChannel = vscode.window.createOutputChannel(channelName);
- channel = refactorSmellChannel;
- } else {
- return;
- }
+ websockets[logType] = ws;
ws.on('message', function message(data) {
- channel.append(data.toString('utf8'));
+ const logEvent = data.toString('utf8');
+ const level =
+ logEvent.match(/\b(ERROR|DEBUG|INFO|WARNING|TRACE)\b/i)?.[0].trim() ||
+ 'UNKNOWN';
+ const msg = logEvent.split(`[${level}]`, 2)[1].trim();
+
+ console.log(logEvent);
+ console.log('Level:', level);
+
+ switch (level) {
+ case 'ERROR': {
+ channels[logType].channel!.error(msg);
+ break;
+ }
+ case 'DEBUG': {
+ console.log('logging debug');
+ channels[logType].channel!.debug(msg);
+ break;
+ }
+ case 'WARNING': {
+ channels[logType].channel!.warn(msg);
+ break;
+ }
+ case 'CRITICAL': {
+ channels[logType].channel!.error(msg);
+ break;
+ }
+ default: {
+ console.log('Logging info');
+ channels[logType].channel!.info(msg);
+ break;
+ }
+ }
});
ws.on('error', function error(err) {
- channel.appendLine(`WebSocket error: ${err}`);
+ channels[logType].channel!.error(err);
});
ws.on('close', function close() {
- channel.appendLine(`WebSocket connection closed for ${logType}`);
+ channels[logType].channel!.appendLine(
+ `WebSocket connection closed for ${channels[logType].name}`,
+ );
});
ws.on('open', function open() {
- channel.appendLine(`Connected to ${channelName} via WebSocket`);
+ channels[logType].channel!.appendLine(`Connected to ${logType} via WebSocket`);
});
}
@@ -140,9 +182,7 @@ function startWebSocket(logType: string, channelName: string): void {
* Stops watching log files when the extension is deactivated.
*/
export function stopWatchingLogs(): void {
- websockets.forEach((ws) => ws.close());
+ Object.values(websockets).forEach((ws) => ws?.close());
- mainLogChannel?.dispose();
- detectSmellsChannel?.dispose();
- refactorSmellChannel?.dispose();
+ Object.values(channels).forEach((channel) => channel.channel?.dispose());
}
From e1ca557b13891e676b58af3e4d955c3ea59f55d0 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Thu, 3 Apr 2025 10:35:21 -0400
Subject: [PATCH 81/83] [DEV] All Plugin Changes (#12)
* initial integration with new changes
* Fixed package-lock.json
* temporarily moved out old logic + files (will slowly add back in)
* renamed to FilterViewProvider.ts
* renamed to SmellsViewProvider class
* renamed to FilterViewProvider class
* cleaning
* some cleaning in SmellsCacheManager.ts
* Removed cache for smells when smells filters was modified
* added warning pop-up if users changed filter
* comment
* fixed when refactor smell by type icon is shown
* added fix for outdated files showing refactor button
* minor change how we process smells in smells view
* made smells have a unique identifier to help make refactoring
* Added error catching for smell detection commands + fixed hashing logic
* fix bug when jumping to smell
* added inital refactoring stage
* added refactoring view
* added diff editor + accept and reject (not working)
* refactoring: removed file path param
* added accept reject icons
* more refactoring
* more refactoring - open diff editor command
* refactor - fixed sidebar dropdown with affected files
* refactor - fixed diff editor to not open in preview
* added energy savings to tree items
* add to focus on view
* added accept and reject buttons
* make files outdated after refactoring
* Add energy metrics dashboard
fixes ssm-lab/capstone--source-code-optimizer#428
* Update energy metrics dashboard
- Added clear metrics command
- Added manual refresh command
- Added listeneer for workspace changes
- Properly integrated dashboard with refactorSmell
- Properly formatted carbon values to use scientific notation
- Sorted tree items to show folders first then files
fixes ssm-lab/capstone--source-code-optimizer#428
* started making icon ui for refactoring
* small fix
* configuring workspace fix
* renamed smells view
* Added LineSelection and Highlighting back in
* Finalized editor decoration modules
- Added back missing settings config
- Refined event handling + cashing
* Hide commands from command palette
* renamed to smellsView
* fixed configuring workspace
* readded the statuses for the smells view
* clean
* clean
* clean
* clean
* clean configureWorkspace.ts
* clean resetConfiguration.ts
* clean
* clean
* readded the smells view
* readded the smells view
* readded the smells view
* readded the smells view
* readded filter view
* bugs
* bugs
* added line selection
* added detectsmellsfolder
* clean
* clean
* clean
* added refactoring
* added refactoring
* added back metrics
* added refactoring
* clean
* clean
* clean
* clean
* clean
* clean
* clean backend.ts
* clean backend.ts and acceptRefactoring.ts
* clean configureWorkspace.ts
* clean detectSmells.ts
* clean refactorSmell.ts
* clean
* clean serverStatus.ts
* clean workspaceModifiedListener.ts
* clean
* refactorSmellsByType added
* toggle linting
* cache bug fix
* commit
* hover manager
* Added back in missing functionality
* Added back in all functionality
closes the following issues:
- ssm-lab/capstone--source-code-optimizer#428
- ssm-lab/capstone--source-code-optimizer#369
- ssm-lab/capstone--source-code-optimizer#499
- ssm-lab/capstone--source-code-optimizer#500
- ssm-lab/capstone--source-code-optimizer#517
* small fixes
* made structuring fixes
* fixed tests for detect, refactor and wipe work cache modules
* added mocks for tests
* removed providers and showLogs from test coverage
* got initializeStatusesFromCache.test.ts to work
* added trackedDiffEditors.test.ts file
* added refactorActionButtons.test.ts file
* Added fields to the package.json to prepare for publishing
* added more tests
* added more tests
* Fixed ui tests and made compilation work
* update readme ssm-lab/capstone--source-code-optimizer#261 (#10)
* push v0.1.1
- fixes ssm-lab/capstone--source-code-optimizer#503
* 0.2.0
* Add real logo
* 0.2.1
* fix icon path
* 0.2.2
* fixed package.json scripts
* Add better comments
* updated configs and dependencies
* remove virtual folders property from tsconfig.json
* Changed workflow testing step to do clean install
* updated test workflow
* added temp purge cache step to test workflow
* updated test workflow
* modified jest config
* fixed module import issue
* Updated publishing workflow + updated version
commit 9f61596224988c21432fa12123edcd2857a429e9
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 19:23:25 2025 -0400
found it
commit 27525517553312d69deb0cea9b94682a8047e0ef
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 19:21:46 2025 -0400
added logging
commit d9518e6908df6f6b2390f47c9e06e7a6095e7e93
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 19:18:34 2025 -0400
try another fix
commit f4a59caf08d6e11cc542b690694be3dee33ac6c4
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 19:16:55 2025 -0400
try a fix
commit 7802389e76d7b19c8c1e7681d8e7eebe2b4038d7
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 19:10:17 2025 -0400
fix typo
commit 6addc6a26612b03d4d9665551d11cc3a91248cf1
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 19:08:57 2025 -0400
fix workflow logic
commit d5f0b0be398b9a8a8f975dfebe933ef9655bd202
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 18:58:30 2025 -0400
Created final publish workflow + updated version
commit 296cf33482cb40fc67ac0b186ff3592a711a530c
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 18:50:23 2025 -0400
Updated release step in publishing workflow
commit 8272d07fe15d30f15deb1b3aca42af1a49f9d842
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 18:40:57 2025 -0400
updated workflow logic
commit f8e3a857c3249893d5fa0e55bb8f1b43c0f020a9
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 18:11:13 2025 -0400
trying one more thing
commit 92178684f9e9deb9dd469d5c41132cdc33f424cb
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 18:09:46 2025 -0400
fix typo
commit 7edd7a9cc69d03412d5d59cb6c0aabc3c6c9d713
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 18:09:16 2025 -0400
trying something
commit ca82e6728207021d1fa21a6bbb1f0ae7355cba33
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 17:53:51 2025 -0400
updated vsce verification step
commit 660887382155cef720cdfa641be695b3b4e09e4c
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 17:48:32 2025 -0400
updated vsce install + trigger event
commit 68a928a995019137a0b8e698a3e2816ed825df1b
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 17:38:12 2025 -0400
found silly bug
commit 188608838e1d72f2aea4f93a1a1c1b4149f7e774
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 17:36:24 2025 -0400
update again
commit f271594f345032b9c40beaa2ca04181f298145c7
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 17:33:44 2025 -0400
update publish.yml
commit 40f4149c3925ebc2d25c8700aeb0745c300ec2d8
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 17:29:22 2025 -0400
updated publishing workflow
commit 57a705f533d9f73ef1f3a5485047230d2367b1d8
Merge: 55eeb78 2e6f904
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Mon Mar 31 17:25:13 2025 -0400
Merge branch 'main' of https://github.com/ssm-lab/capstone--sco-vs-code-plugin into test-publish-workflow
commit 2e6f904904db1b5d46696be483c187edf29c869b
Author: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri Mar 28 09:21:36 2025 -0400
Create publish.yml
* Added configureWorkspace.test.ts
* Added resetConfiguration.test.ts
* Added refactorActionButtons.test.ts
---------
Co-authored-by: Nivetha Kuruparan
Co-authored-by: Tanveer Brar <92374772+tbrar06@users.noreply.github.com>
---
.env | 12 +-
.github/workflows/jest-tests.yaml | 5 +-
.github/workflows/publish.yml | 58 ++
.github/workflows/version-check.yaml | 59 ++
.gitignore | 1 +
.prettierrc | 1 +
.vscodeignore | 26 +-
LICENSE | 9 +
README.md | 67 +-
assets/black_leaf.png | Bin 0 -> 979 bytes
assets/darkgreen_leaf.png | Bin 0 -> 1292 bytes
assets/eco-icon.png | Bin 0 -> 518 bytes
assets/eco_logo.png | Bin 0 -> 12250 bytes
assets/green_leaf.png | Bin 0 -> 1288 bytes
assets/white_leaf.png | Bin 0 -> 965 bytes
data/default_smells_config.json | 101 +++
data/working_smells_config.json | 101 +++
media/script.js | 122 ---
media/style.css | 42 -
media/vscode.css | 91 --
media/webview.html | 30 -
package-lock.json | 30 +-
package.json | 680 +++++++-------
src/api/backend.ts | 248 ++++--
src/commands/configureWorkspace.ts | 146 +++
src/commands/detectSmells.ts | 121 ---
src/commands/detection/detectSmells.ts | 193 ++++
src/commands/detection/wipeWorkCache.ts | 30 +
src/commands/refactor/acceptRefactoring.ts | 110 +++
src/commands/refactor/refactor.ts | 132 +++
src/commands/refactor/rejectRefactoring.ts | 47 +
src/commands/refactorSmell.ts | 323 -------
src/commands/resetConfiguration.ts | 35 +
src/commands/showLogs.ts | 311 ++++---
src/commands/toggleSmellLinting.ts | 50 --
src/commands/views/exportMetricsData.ts | 76 ++
src/commands/views/filterSmells.ts | 79 ++
src/commands/views/jumpToSmell.ts | 35 +
src/commands/views/openFile.ts | 21 +
src/commands/wipeWorkCache.ts | 52 --
src/context/SmellsCacheManager.ts | 194 ++++
src/context/configManager.ts | 39 +
src/context/contextManager.ts | 33 -
src/emitters/serverStatus.ts | 71 ++
src/extension.ts | 837 +++++++++++++-----
src/global.d.ts | 116 ++-
src/install.ts | 310 +++++++
src/lib/README.md | 3 +
src/lib/dependencyManager.ts | 77 ++
src/lib/processManager.ts | 134 +++
src/listeners/workspaceModifiedListener.ts | 201 +++++
src/providers/FilterViewProvider.ts | 269 ++++++
src/providers/MetricsViewProvider.ts | 421 +++++++++
.../RefactoringDetailsViewProvider.ts | 201 +++++
src/providers/SmellsViewProvider.ts | 304 +++++++
src/types.d.ts | 57 --
src/ui/fileHighlighter.ts | 145 ++-
src/ui/hoverManager.ts | 168 ++--
src/ui/lineSelectionManager.ts | 103 ++-
src/ui/refactorView.ts | 198 -----
src/utils/TreeStructureBuilder.ts | 117 +++
src/utils/configManager.ts | 68 --
src/utils/editorUtils.ts | 22 -
src/utils/envConfig.ts | 29 +-
src/utils/handleEditorChange.ts | 126 ---
src/utils/handleSmellSettings.ts | 53 --
src/utils/hashDocs.ts | 31 -
src/utils/initializeStatusesFromCache.ts | 108 +++
src/utils/normalizePath.ts | 8 +
src/utils/refactorActionButtons.ts | 71 ++
src/utils/serverStatus.ts | 35 -
src/utils/smellDetails.ts | 74 --
src/utils/smellsData.ts | 166 ++++
src/utils/trackedDiffEditors.ts | 35 +
test/api/backend.test.ts | 339 ++++---
test/commands/configureWorkspace.test.ts | 84 ++
test/commands/detectSmells.test.ts | 442 +++++----
test/commands/exportMetricsData.test.ts | 186 ++++
test/commands/filterSmells.test.ts | 150 ++++
test/commands/refactorSmell.test.ts | 601 +++++++------
test/commands/resetConfiguration.test.ts | 58 ++
test/commands/toggleSmellLinting.test.ts | 96 --
test/commands/wipeWorkCache.test.ts | 165 ++--
.../workspaceModifiedListener.test.ts | 269 ++++++
test/mocks/context-mock.ts | 31 +
test/mocks/contextManager-mock.ts | 53 --
test/mocks/env-config-mock.ts | 25 +-
test/mocks/vscode-mock.ts | 233 ++++-
test/setup.ts | 6 +-
test/ui/fileHighlighter.test.ts | 349 ++++++--
test/ui/hoverManager.test.ts | 348 ++++----
test/ui/lineSelection.test.ts | 284 +++---
test/utils/handleSmellSettings.test.ts | 134 ---
test/utils/hashDocs.test.ts | 56 --
.../utils/initializeStatusesFromCache.test.ts | 167 ++++
test/utils/refactorActionButtons.test.ts | 81 ++
test/utils/smellsData.test.ts | 211 +++++
test/utils/trackedDiffEditors.test.ts | 115 +++
tsconfig.json | 2 +-
vsc-extension-quickstart.md | 48 -
webpack.config.js | 17 +-
101 files changed, 8809 insertions(+), 4009 deletions(-)
create mode 100644 .github/workflows/publish.yml
create mode 100644 .github/workflows/version-check.yaml
create mode 100644 LICENSE
create mode 100644 assets/black_leaf.png
create mode 100644 assets/darkgreen_leaf.png
create mode 100644 assets/eco-icon.png
create mode 100644 assets/eco_logo.png
create mode 100644 assets/green_leaf.png
create mode 100644 assets/white_leaf.png
create mode 100644 data/default_smells_config.json
create mode 100644 data/working_smells_config.json
delete mode 100644 media/script.js
delete mode 100644 media/style.css
delete mode 100644 media/vscode.css
delete mode 100644 media/webview.html
create mode 100644 src/commands/configureWorkspace.ts
delete mode 100644 src/commands/detectSmells.ts
create mode 100644 src/commands/detection/detectSmells.ts
create mode 100644 src/commands/detection/wipeWorkCache.ts
create mode 100644 src/commands/refactor/acceptRefactoring.ts
create mode 100644 src/commands/refactor/refactor.ts
create mode 100644 src/commands/refactor/rejectRefactoring.ts
delete mode 100644 src/commands/refactorSmell.ts
create mode 100644 src/commands/resetConfiguration.ts
delete mode 100644 src/commands/toggleSmellLinting.ts
create mode 100644 src/commands/views/exportMetricsData.ts
create mode 100644 src/commands/views/filterSmells.ts
create mode 100644 src/commands/views/jumpToSmell.ts
create mode 100644 src/commands/views/openFile.ts
delete mode 100644 src/commands/wipeWorkCache.ts
create mode 100644 src/context/SmellsCacheManager.ts
create mode 100644 src/context/configManager.ts
delete mode 100644 src/context/contextManager.ts
create mode 100644 src/emitters/serverStatus.ts
create mode 100644 src/install.ts
create mode 100644 src/lib/README.md
create mode 100644 src/lib/dependencyManager.ts
create mode 100644 src/lib/processManager.ts
create mode 100644 src/listeners/workspaceModifiedListener.ts
create mode 100644 src/providers/FilterViewProvider.ts
create mode 100644 src/providers/MetricsViewProvider.ts
create mode 100644 src/providers/RefactoringDetailsViewProvider.ts
create mode 100644 src/providers/SmellsViewProvider.ts
delete mode 100644 src/types.d.ts
delete mode 100644 src/ui/refactorView.ts
create mode 100644 src/utils/TreeStructureBuilder.ts
delete mode 100644 src/utils/configManager.ts
delete mode 100644 src/utils/editorUtils.ts
delete mode 100644 src/utils/handleEditorChange.ts
delete mode 100644 src/utils/handleSmellSettings.ts
delete mode 100644 src/utils/hashDocs.ts
create mode 100644 src/utils/initializeStatusesFromCache.ts
create mode 100644 src/utils/normalizePath.ts
create mode 100644 src/utils/refactorActionButtons.ts
delete mode 100644 src/utils/serverStatus.ts
delete mode 100644 src/utils/smellDetails.ts
create mode 100644 src/utils/smellsData.ts
create mode 100644 src/utils/trackedDiffEditors.ts
create mode 100644 test/commands/configureWorkspace.test.ts
create mode 100644 test/commands/exportMetricsData.test.ts
create mode 100644 test/commands/filterSmells.test.ts
create mode 100644 test/commands/resetConfiguration.test.ts
delete mode 100644 test/commands/toggleSmellLinting.test.ts
create mode 100644 test/listeners/workspaceModifiedListener.test.ts
create mode 100644 test/mocks/context-mock.ts
delete mode 100644 test/mocks/contextManager-mock.ts
delete mode 100644 test/utils/handleSmellSettings.test.ts
delete mode 100644 test/utils/hashDocs.test.ts
create mode 100644 test/utils/initializeStatusesFromCache.test.ts
create mode 100644 test/utils/refactorActionButtons.test.ts
create mode 100644 test/utils/smellsData.test.ts
create mode 100644 test/utils/trackedDiffEditors.test.ts
delete mode 100644 vsc-extension-quickstart.md
diff --git a/.env b/.env
index 86798f4..8cafc30 100644
--- a/.env
+++ b/.env
@@ -1,6 +1,6 @@
-SERVER_URL='127.0.0.1:8000'
-SMELL_MAP_KEY='workspaceSmells'
-FILE_CHANGES_KEY='lastSavedHashes'
-LAST_USED_SMELLS_KEY='lastUsedSmells'
-CURRENT_REFACTOR_DATA_KEY='refactorData'
-ACTIVE_DIFF_KEY='activeDiff'
+SERVER_URL='127.0.0.1'
+HASH_PATH_MAP_KEY='hashPathMap'
+SMELL_CACHE_KEY='smellCache'
+WORKSPACE_METRICS_DATA='metricsData'
+WORKSPACE_CONFIGURED_PATH='workspaceConfiguredPath'
+UNFINISHED_REFACTORING='unfinishedRefactoring'
diff --git a/.github/workflows/jest-tests.yaml b/.github/workflows/jest-tests.yaml
index 60c8bc3..c71a146 100644
--- a/.github/workflows/jest-tests.yaml
+++ b/.github/workflows/jest-tests.yaml
@@ -18,8 +18,9 @@ jobs:
with:
node-version: 20
- - name: Install dependencies
- run: npm install
+ - name: Clean install
+ run: |
+ npm ci
- name: Run Jest tests
run: npm test -- --coverage
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..611807c
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,58 @@
+name: Publish Extension
+
+on:
+ push:
+ branches: [main]
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # Needed for tag/release creation
+ id-token: write # For OIDC auth
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # Full history needed for tagging
+
+ - name: Get version
+ id: version
+ run: |
+ VERSION=$(node -p "require('./package.json').version")
+ echo "tag_name=v$VERSION" >> $GITHUB_OUTPUT
+
+ - name: Create and push tag
+ run: |
+ git config --global user.name "GitHub Actions"
+ git config --global user.email "actions@github.com"
+ git tag ${{ steps.version.outputs.tag_name }}
+ git push origin ${{ steps.version.outputs.tag_name }}
+
+ - name: Install dependencies
+ run: |
+ npm install
+ npm install -g @vscode/vsce
+
+ - name: Package Extension
+ run: |
+ mkdir -p dist
+ vsce package --out ./dist/extension-${{ steps.version.outputs.tag_name }}.vsix
+
+ - name: Create Draft Release
+ uses: softprops/action-gh-release@v1
+ with:
+ tag_name: ${{ steps.version.outputs.tag_name }}
+ name: ${{ steps.version.outputs.tag_name }}
+ body: 'Release notes'
+ files: |
+ dist/extension-${{ steps.version.outputs.tag_name }}.vsix
+ draft: true
+ prerelease: false
+
+ - name: Publish to Marketplace
+ run: |
+ vsce publish -p $VSCE_PAT
+ env:
+ VSCE_PAT: ${{ secrets.VSCE_PAT }}
diff --git a/.github/workflows/version-check.yaml b/.github/workflows/version-check.yaml
new file mode 100644
index 0000000..a92a9a4
--- /dev/null
+++ b/.github/workflows/version-check.yaml
@@ -0,0 +1,59 @@
+name: PR Version Check
+
+on:
+ pull_request:
+ branches: [main]
+
+jobs:
+ validate_version:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout PR branch
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.head_ref }}
+ fetch-depth: 0 # Required for branch comparison
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - run: npm install compare-versions
+
+ - name: Get PR version
+ id: pr_version
+ run: |
+ PR_VERSION=$(node -p "require('./package.json').version")
+ echo "pr_version=$PR_VERSION" >> $GITHUB_OUTPUT
+
+ - name: Fetch main branch
+ run: git fetch origin main
+
+ - name: Get main's version
+ id: main_version
+ run: |
+ MAIN_VERSION=$(git show origin/main:package.json | node -p "JSON.parse(require('fs').readFileSync(0)).version")
+ echo "main_version=$MAIN_VERSION" >> $GITHUB_OUTPUT
+
+ - name: Validate version bump
+ run: |
+ # Write a temporary Node.js script for version comparison
+ cat > compare-versions.mjs << 'EOF'
+ import { compareVersions } from 'compare-versions';
+
+ const mainVersion = process.argv[2];
+ const prVersion = process.argv[3];
+
+ console.log("Main version:", mainVersion)
+ console.log("PR version:", prVersion)
+
+ if (compareVersions(prVersion, mainVersion) < 1) {
+ console.error(`::error::Version ${prVersion} must be greater than ${mainVersion}`);
+ process.exit(1);
+ }
+ EOF
+
+ node compare-versions.mjs "${{ steps.main_version.outputs.main_version }}" "${{ steps.pr_version.outputs.pr_version }}"
+
+ echo "β Version validated"
diff --git a/.gitignore b/.gitignore
index 4a6b8bd..5516fa6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ node_modules
.vscode-test/
*.vsix
coverage/
+.venv/
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
index ed1cfa5..21a17ba 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -3,6 +3,7 @@
"printWidth": 85,
"semi": true,
"singleQuote": true,
+ "endOfLine": "auto",
"tabWidth": 2,
"trailingComma": "all",
"plugins": ["prettier-plugin-tailwindcss"]
diff --git a/.vscodeignore b/.vscodeignore
index d255964..c80888d 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -1,14 +1,18 @@
-.vscode/**
-.vscode-test/**
-out/**
-node_modules/**
src/**
-.gitignore
-.yarnrc
+.vscode
+node_modules
+package-lock.json
+tsconfig.json
webpack.config.js
-vsc-extension-quickstart.md
-**/tsconfig.json
-**/eslint.config.mjs
+eslint.config.mjs
+.prettier*
+.gitignore
+run/**
+.venv/**
+test/**
+.github/**
+.husky/**
+coverage/**
**/*.map
-**/*.ts
-**/.vscode-test.*
+.vscode-test.mjs
+.env
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4d87f17
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,9 @@
+Copyright (c) 2024-2025 Ayushi Amin, Mya Hussain, Nivetha Kuruparan, Sevhena Walker, Tanveer Brar
+
+Permission is hereby granted, on a case-by-case basis, to specific individuals or organizations ("Licensee") to use and access the software and associated documentation files (the "Software") strictly for evaluation or development purposes. This permission is non-transferable, non-exclusive, and does not grant the Licensee any rights to modify, merge, publish, distribute, sublicense, sell, or otherwise exploit the Software in any manner without explicit prior written consent from the copyright holder.
+
+Any unauthorized use, modification, distribution, or sale of the Software is strictly prohibited and may result in legal action.
+
+The Software is provided "AS IS," without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, or non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the Software or the use or other dealings in the Software.
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
\ No newline at end of file
diff --git a/README.md b/README.md
index fe74f71..c723736 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,55 @@
-VS Code Plugin for Source Code Optimizer
-
-1. clone this repo
-2. open terminal and write:
- `npm install` (only the first time)
- `npm run compile` | `npm run watch` <- second command will auto compile on save
-3. open another vs code window with the ecooptimzer repo
-4. start venv in the ecooptimizer repo
-5. run "python -m ecooptimizer.api.main" in terminal to start the developement server manually
-6. come back to this repo, go to run and debug (or just click `F5` key)
-7. run extension (should open a new vs code window so open the repo u want in this)
-8. in the vscode search bar (`ctrl+shift+p`) type ">eco: detect smells" and run it
+# EcoOptimizers - Sustainable Python Code Refactoring
+
+EcoOptimizers is a VS Code extension that detects and refactors inefficient Python code, reducing unnecessary computations and lowering COβ emissions. By identifying common code smells and providing automated refactoring suggestions, EcoOptimizers helps you write cleaner, more efficient, and environmentally friendly code.
+
+## Features
+
+- **Detect Code Smells** β Automatically analyze your Python code to find inefficiencies.
+- **Refactor Code Smells** β Get instant suggestions and apply refactorings with ease.
+- **Reduce COβ Emissions** β Improve computational efficiency and contribute to a greener future.
+- **Seamless VS Code Integration** β Analyze and optimize your code directly within the editor.
+
+## Supported Code Smells
+
+EcoOptimizers detects and refactors the following common code smells:
+
+- **Cache Repeated Calls** β Identifies functions that repeatedly compute the same result and suggests caching techniques.
+- **Long Lambda Functions** β Flags excessively long lambda expressions and converts them into named functions for readability and maintainability.
+- **Use A Generator** β Suggests using generators instead of list comprehensions for memory efficiency.
+- **Long Element Chain** β Detects deeply nested attribute accesses and recommends breaking them into intermediate variables for clarity.
+- **Member Ignoring Method** β Identifies methods that do not use their class members and suggests converting them into static methods or external functions.
+- **Long Message Chains** β Finds excessive method chaining and refactors them for better readability.
+- **String Concatenation in Loop** β Detects inefficient string concatenation inside loops and suggests using lists or other optimized methods.
+- **Long Parameter List** β Flags functions with too many parameters and suggests refactoring strategies such as grouping related parameters into objects.
+
+## How It Works
+
+1. **Detect Smells** β Run the EcoOptimizers analysis tool to scan your code for inefficiencies.
+2. **Refactor Suggestions** β View recommended changes and apply them with a click.
+3. **Optimize Your Code** β Enjoy cleaner, more efficient Python code with reduced computational overhead.
+
+## Demo Videos
+
+Watch EcoOptimizers in action:
+
+- [Detecting Code Smells](https://drive.google.com/file/d/1Uyz0fpqjWVZVe_WXuJLB0bTtzOvjhefu/view?usp=sharing) π
+- [Refactoring Code Smells](https://drive.google.com/file/d/1LQFdnKhuZ7nQGFEXZl3HQtF3TFgMJr6F/view?usp=sharing) π§
+
+## Installation
+
+1. Open VS Code.
+2. Go to the Extensions Marketplace.
+3. Search for **EcoOptimizers**.
+4. Click **Install**.
+5. Intall the `ecooptimizer` python package.
+ - run: `pip install ecooptimizer`
+ - run: `eco-ext`
+6. Start optimizing your Python code!
+
+## Contribute
+
+EcoOptimizers is open-source! Help improve the extension by contributing to our GitHub repository: [GitHub Repository](https://github.com/ssm-lab/capstone--source-code-optimizer)
+
+---
+
+π Start writing cleaner, more efficient Python code today with EcoOptimizers!
\ No newline at end of file
diff --git a/assets/black_leaf.png b/assets/black_leaf.png
new file mode 100644
index 0000000000000000000000000000000000000000..fdd8163192dfea2ffe4da78f84d9bfba15b4ecc0
GIT binary patch
literal 979
zcmV;^11$WBP)%#6)TWVA(VF_ZGaD3+R#LXk#!Y4(Be
zfqd}x0m<5$hA^SwgA`gJg_V-mY2_se*@vN+mzcM_#n?|LXLY-r=XuV3o_jO({69XO
z>wo>P`L}68MZ6r+`jg
zM*+|o7!OndAF;#6jdS0yJ3&eGVK0RvDJm1E5=b3Qq=B10Q0D
zZU9PwJQ2UtFt((<#7_n4qlo?r90CfWZBXE}i2DL$w59OA!0{-;-vVX8Kuf-CU92B_
z+Dd#r@HB$(_rMw;&4IU6aD5AX7Jx!My%H_JVcRbrfx9>GPVjCDQ~Y8rRN@{mHl!To
zg1Zq&3WIo_?$Hwalr;!LyaF&-TMIv?c<}%jW!PZNz_PfABj-CSa_x=R!!#Jg7Y5`>
z0u})|7GsMWG
z4baokl|B(%(__SJ6XSQe6mU?GR0JmZ%`;K(C~KW!(e?bOaY?LhOMXmnEZ~ITIcgD;
zCt~Vd63g|U?!b|4=&K;lxo3#@%Ps{BGT7&t7ID=gM!DG00dtoLj_rX9&_p$IEdrTQ#;-6eovjXe
zF9?pgt_9>8+UXr&K!g)lcAcBH8@Clemf`M^<+%dbZ1}3m+f4S28L2PwetfdA&lbz4
z&Wi`yjLCJ~iYxl6`9l2O;%K|Gbm8$iQ3G*`p|3~7dWRfIz-oRG98R!B`(tk7E(vQEYi#-C)0bA4|
z7`kvFLfHZIW~(yUhz<>4zJB_4etAwHfdsrZ!!P2OvZew|IvxN3002ovPDHLkV1iNo
B#fty{
literal 0
HcmV?d00001
diff --git a/assets/darkgreen_leaf.png b/assets/darkgreen_leaf.png
new file mode 100644
index 0000000000000000000000000000000000000000..1b5d1ea29880db6754dfe0db9f908af0fe38bf82
GIT binary patch
literal 1292
zcmV+n1@roeP)?`s8#Ca3f-=j=8%TEQPiK}D#j2r4L|
zf)8qcDC!TzAHII5ES46fT58vepaukSeI&Uv*VQUkEYd2}Cq7!GRjD;y
zIhzf+JA1Qx@7u&OFX{W*ui<=H<|qC7*6S7Pf3S(K%G4Ykd9D;L%ke
z_!KaHWCZ^Orq6r0|9EGviUnr`R~g3F4C9|hsG=7LzV1{ihdFabD!Bz&uXr&C6+viE
z7--Y5@Uc0O(;23F4AYKiplNzBO<1S@7vMdg^q&Gus}0kyt%2eXM(8CFyuTr(7{BcK
zs{g2i27LL};Uf*x10eKA3((sQ;~JN-FSy?@-PKSJOsfwHc<}K`VA|Fsr~d|~=Z#-j
z(X2p$=?%5_ukO)&=d1zm8Xr2z2))we#CI8?wIBnhM$zZ`n6=KTh!ti9yz3V(0>N)X
zTOs4`hVjAWrGZ6p^%}-CO4q*Bqu_dYy$x?jejfm#%~4N0OtR(T35IF6(wj>Sd`9pN
z3pAwlZ3V^)(y(Eeo=`RPom0wnw5=GJZnix-rE3pT`c&XB}2TGe3w(NRRm$oCZ
z4Ntz70){=hB#8rt@l_Sc7hCOd8T(WOwk0rJpa0o0TpU+NjpNkybxTEB?aPkgVpTK0
z#CE`Pk)VEq^xMP!<|Gvme7xaQnRaG;ynO1Lj8{0P_K**TeQy-{+%kQHJf|(Gk};Ln)6w`~
z@~KHbD)0xA&~B)nKJIOsi8d%MZCV&IyP0u;&?B*plyV)0@f{WEpVND?R&om_AKmKB
z+K^uOY!dJg7G=hjPTG?33+FeCKc-ve3)a!8Z*9lwmVvWdURsQ!A-YD=2QT?$Ao$@7
zr@tWxzOP(7!Z3~5E@q=*M=zG+REe68y&1t9E!Eatu9o-Tq&n0(tz0~&b~A?zwP?b4
zbf4a19g12rrao=9FC8D~NdhQPP#qetodK;LYw3nbW
zn_}retv&9uK*DImoE8wg#g4w)nLN+Syv&O;Yw{oO1lbyN70`zO0000kB1_Y{X&%F<|@yd_>XRN(7T4Z5kmF
z(h6Hx#6}TOv=GEj5v)|u(nf=%45SJnn5_&+5%C3Dgh&!y9}Jv@kIQ|nY@-KmGv~~i
z@6P>ZF5CfBmD(@?q=7jg2mAqAN@4sMm;!zR*PiDGPVS78O-NOTa=lC(4*J@HY+7sCpCkZ3SOGb@eFfs2gdpTi35
zOY|s!uLVw|>GW_Z#Sg;@{Ff+b{NIVUetS_V&gLZ$dlEhM;Zn}&U;6M#ai_$%&yzmf
zhB%uKKXi!S2`kX0sr(1@I`JVT{|}|^8J}cyOe1BD<+0v4(QVyM9a5y4fIjx?hnC6t697J^Yx(V&qg6_j
z=AxMhg3KnluiJ_sD7oTSO%)*R!oCymXGXlce-eUV9w>fMzpcRNBFHc5QQQ2eex92M
z5tLZ-u$`1}vUy5uJYXY;gL6uJSVT0LY7kD|9Tn$jGF0DWVi2{{(Ztur)6z43EjcpE
zJvEW+o$9qMA~iZ<%}x_%r8Vk#n7Asep0ft%9-Z*MlidkHe5m1S+hFK($Z{~b%dpv
zwUw2PS-52w*=#3ywUyP%HEZmwEyMmH^mEC7lGzXrRBTq-thBXRV`Xi%dX0_UzjmIU
z{7?LC6k0^2;wVll|DpRY*fg$#g+jV9Q9p$;J@{KH|E%~avY&|mrk4M{s&+>Fqo#OT
zV$8G&?2ND=$B<*maa4tpto~Ng&IkfEiW)=ymx_on)c;fw*d8G!EN-`>NeT#VR~RjZ
zYU1P?Ln9l6#qBikC&$E4=mwsYo#ezggSGI-z?-s*N)JmU8*HNNrWn8hqISm_m>GaX
z{*ROYy_ti>zwz<^aqjqkvwnqrPCKE(GGPJ^z3T5n!-znnQ6njdPTRublF8&m1J}f`
zLaJwEL3kqQz|mC!aSO5b^xbVhCCA3c
zfUh|IziPP%eC>Uv*DhWD8vp+M)Q|kJgKW>u
z9MR-;-1jFR&*!})B1m%swn!QN^je7#WF3QuMhukH2?(MU%10r`5GDq3QPH6y$dYD`
zGNOr6#Uczf%l{^7$u(M81vl@yzJ1*A7gpf@Np@Z1t4PZ`aU~rn#PH5z0+Q<$puLY2
zVEd&jP_W{LzQ^hb8!PuW7ekS`$B7x|E6sB}2(AJ{_SbWjL4rp14)V)i5Pd7LcvFD(
zaqcQy-V%upDKjP$G0;~h9J-Jy{jR)7W6Y{`4WenLiY@k#h65J9!A(#GApR_MLckee
zivwwhEhmyp7JWcmhAhRoXDiKj7%l5e`Gaor1jTq2&L1w|(h`cen>_-eL`p~~-^wE@
z_!3U?nf)pp#wi#kYmhB$vBw7+NGzobkmgxd;@m9(+C!)1_)`{$GLrkNI^p_n!WIRW5#rxZ=#uxY^UA2>!k$#hG()!8=7LM2%U|yU(CF)6u3eQvFi_es7LDp_3O;eW4s!
zL8$4(p^QxYU>p`TI)_wPWVCC=!wQSlXCBnQ+
zh9HOAIBl^@qi)hv5CaEQ$E{JpAB`kRQn0kuI`Wj@NGyVgF-(2n`0H>O
zxiqT~!A79Rm@TVbkmZg4vxbE*qIwN(&iM9SSIMbHS3T9is~qMf*5of+q(`0l%|=u1
zynuZ}Gl*F(3RCUw<0(&%?kB*$NT7PE-m4g3_;w4YZNOcMAPnFT0}dqDW6FX8v?-eT
z&S=J~2>zt~a|NBOS?%BG3|h0wK6e3uG?h&)nf@*7V>ziIT~5b*-0EF=Uyi6Lowj6aRE42s4oBSIbO6s7_r
z?pj3BW~tsUELw|%vMt3d&)|`&%}1}^{S+%+`ze-a>Qtomls-F`KPG@dl4q#!g{uxYJU^rIOoebi6WhriKu@j$zv(XfD
zvtjO}cFx#k8iJ%e;MjxV>I-KZo>aYmhNi55Ow51NGuTvxL6A}%YE!tLye|-qFnrZ{
zA@zZ1mH14>x)#nvRWreW(d75{loe*&5wy}jfA#L+6}*~WH|D@q;q2U}8%Ul0ZjXJu
zeKym_Qc0JK#Uo#;KZSbx+%=lKeTh1j#E4=awa4@Tww>M@=>
zAib|ishgPJqebw|P-d(N77bypPJ^A;HO&@_n
zwpXyW`_em^Z;YF`VdDMDhZXAtb!HAPRxL{7#$4sjezw0M?6VtiR{iM$Vyw;tMCPkD
zBl$YPttTPY9o*qImBnoNr{|@k9^>_`zS8=NWrf$J=19_g5K#~3^D23q*~QdN;CNry
z!bpct(YQ%}!Jw!AWYt9nqyi)aNY2q%#s_}`iJ5nn=XI8BiZlF2-*U+=QYX0Tw$60q
zau#uPMfKU&cCMpu#5dcFNtc6f43opso@T!oQok^5yxmXXFjHli=1WDnsYPBRABWf_
z+0^ykKJ-PlsD^tWhqE9DLPNUhq3+FsG4_+qBOi@PeP^g-Z=bAOAJiGC$nD6Il^sAu
z?@F5_o)mIU9?#z036YLV)6`s{iamSCNd7I)RC4rYnDb+T>ap2O7U{0PU^1bcdy(xh
z5ysw)4s1ipLiyN@(k0cp74GV(i}Q>nLR?3Q*GLRSs?%(*D!S42B>tR^+{u2e-~z70
zX(v`mp`!;JBinip-jatEi%s@ccM()S9V3n|Yt7P4yszmlxXF$KX17T-n{^dj5Zk8A
z&dA^3I?T&3D2Fh<)iNDwjHC#*@fg4*lDtpV1TgrR=Vho~|RA@WUXsuZZa0-7tTL$Vz5b<$+F#V_^@KVP>b8$
zJN97tD^;A@abAdW@deqv4+Nt5SB$cs6)Q7J
zfS*AH(=5K$qXJyVD@byX??#tZ6Z=B3jNgkZF$k}jGf|m2?f-`#aIR_tjpPbUJylOLgs&M7Kyc2ya)wvdqdc
zkx$PGQQ{+$Y=@rv1SPd))jjcFTf(J`1z*Mnzn)?rwE5GykNAcjwxjW-QFWnmfL)NF
zN~qpRJ6uO*4L2u{z3Or(4wbKkw`I
zSUkIhbS%7=4kB|`=Q(>6L%^%0xhO*CdVv|c5l;fsUM4a9@>y5_F%@xr&9zJWS`9*R
z6Jz&0`m6PN(UY9YEG{Krl={);Zz}49_eIMJt33S=xC*2=QH8S>v#?XZDuVg8J(
z!Jhu27uhcc*o}Kcf+M&NyNzy@I@GkUHJHekMncr-3+3aNzF+FK)t(3dL0XD`-06l`
zqY;P>1=6$d!EqujznGzT)J2$jkUXP3qe%JFrRzni>>rULtzTpE=b&uE_*jxOeDV5Z
zEIr|{)A3g;B+I17Ty&`GGKr(_;-w5kR)cB&Y(u7HaRof1)72#e^d>!3(K(|cT8z8tqk6S)%;_Q6fu|w@GcgAs2bBOzh1Ix3N2+J;XDncPY&ul3pamsu*q
zh(cM4k3l|P7oeT7w6SK^R2Y`AaPkRcwFd-5?a0N7Z)E}))E6mRUWF8R`1ISC1>m={
z<-MPA2W%fCQLckj%bH7389G#F9V+$KvcfNr;kNJZbO~9y><^B|h0;&Hz4fkhm@Msn
zGdfzp^DFvx@SwSsSih(YqZXC?GG;`wK30FB?OSMZ=sXzUlDeZ!;s8AwuEtb9Az^}wBI+qmrK@5dJO4rpu{Ukp8=
zsi}{meSCfLf!5*7n;YE^@oyWT&0hTeD~IEr5TJGDUMgdO=2NADKXQE5`<+;^;)rWz
z_0B;d6)$vsd4)COnD^3!OR`%Mm!4R^aZTmZa705*&Gg3U!zq!2i#wj>tTf!KG3wOR
zFf@5^*Q){Nk-YTtD@J9F_3EijdEoeG_Fce*#A4Hz?QJEvY!&3Z5P^^NB|L1n@tSuG
zKS&WhJwx$BhdvTQhRxc4Z1Iszw
z$GiwvC=^3mb*RnCciKhnLE34?5_O=#>mnelTBlJ>OiMENvdz!QZKXLg#=XwIoeIaM
zFN~O`KX+bd%L;t3S(Go#P28a&yN*mVz8NUpa39a+niU5ce_Te=_IC^Q(UISrIM3C!
z|FJ6NsfO(_;*ejEc0FmlfZI?Uc+xbEeTkkvaf+B3vb*9!Fb6Sof
zwe{rW2CR@&2(@x;RI#5BrVJLFY$SNzuixBgnD!Dk=Kar<)2ufp!U)oYN9t8>r^khe
zMK2ah@n+R5#$4xW401b4eSBVv;$kLaf7O}#xGc)~+3yDfpX0{f2({u5WmO;`9y}oY
z##P3fovu8^Ew&jq9@eDK*
zlft0a-LJ9-X+Iz+d0B}WXdPZSV6ZT9lStO
zA>5szI2cA-NFwwSl=6ORFn)mP$xiEl@c`StA8zIq+igfc$Wk@xi4dX+7*m9;c`W<;
zHUO{`qR%8-dat;Eb8aOl%`JQKtz#-a`&yRW54!oD_7j0K)MheS=WEMIBYowp<_CWH
z{Q8udyK{zP5T*Ng`lilPY)load7VGNKS6+Nw(%0@vgTiUjD9$N_|-3}*t-LVSvSX(
zBAom#O{+im1m2Uq7;?96tbcJ?oVYm?@_HwfQjgkz1*l8nV-zl8BfIkEh=Z}@cQ|-C
zhG^liOrl+Eq!-TD((@8`ou+K;K5NyMR8)nDkD2vz*ObQ37je!l1f_=i9A(Eg&YLuc
zt54m0%xhrvk*}e8Ojf_8`1A&g%)M!6-)i_}aB4sufTz(DK+2C~?>|mcOSPyZ=0{_Z
z+E$K6XD7ZPYbZD3glqlV#%naBe-YEkp<^SVf;7@1H2kzN(nNj^{IFy=I{JRadG44{
z>l4O=%K4xpA2@F$>8&-Fosx#9Np(`*L`ji0yDLU^>f`Zd*;MZ!ZI3s+E+Ij+p{ASA
zTviO1Q#iif3Q1$F`5dXHyxUudV#UTzbFp@jX!D(=C7EQQp}3fDUS|P;Gbn;T9MtxJ
zmOu~EZrX`G^eT*>ei>l>OQzIK(W$?we-qDt2LKPtwn93i${zogUoOGxP2NE0fl`z`^AKTd4hc4tLN
zX7n3g|E%rGFLGX_9vQ8+Q^gXY0^VN6)qTzDKcaVkem8j(3yM6aL*3M-i!Z*-Vw`U)
zR@Uoz`I829P`k4wQL2_3%1_si;9JdY!gjVm5tv)M*dJ}z$}w81PS~~lxb$^av61B#
z$j)|O13;~Ku-yc&Anl3ayKw|ee
z5!LVA1N1vvf~1;Ma|L|z0_bigxX2DONjO-%Y@ZN?O@V8o0_gi{ZfDC3p_bSy&_}~>
zH8s>3Hl44EHP2hz*-{Pg=V^XuJ6nrK0%oxh0(D%iwXa~W0)7Cf*g+VLPbX$X&)D36
zj=e^6L1Un@2?7uI*+|$VNML)hsYkgR1%J-`TyZA2zL|jJ)jcC{lxJNvot!UK3$3=T
zzrM~;NyJlF!_Tw?*hozM)M&Onl*>Xt_gm=H^IWHCpwVGor#T5{;
zS!~QX>g^5r+VALPQyr6@Yzx5Nw(a5S6NBN8s(r`4X9oO)JN51zJqL8grt#&S1QiD5^dO>v4
ztWeW=9kV&XHJ@YraB&>_y9+68k7#AsO&W5k{w0C4_6C&0tL}1iBiY|gNhX2xP5d(n
z!H;WL4DU{0W9g}K>v9%yJ(IXvfE$}bT02H~sYh}PaBIGK8XXM1AjFC-wcN;0jUyRX=uker%*
z?fd$9J3{&U3P>X~wo|C+X;}mKkFrh}+&e5$?FFXT?s}tsLrCoxa8pOQ9f5S}$2=h_
ze$RPY^I_8uhHK1#9p0I2y$IiwfhNg(E?M8+o>m1itcH0ojk5l=nQzbMvw}M)8yLX4*%^^h>8yW8m
zB&}ZWpZYsZ?bCks)U>=tz-b8y_xLqcIQj~N>E8LS8ccfb20n^mN48KcEe7g+TQ_h;(Ub`iyY?WWp&3EjwbdjJQhjm)d*vo+@&z7NgviuyWHw+=6wHMWl3ehS0H^)1B#OcSXY`&}X$f;RR1w4a=0BtB1h7ua1ToUo|V-DA+2+80>^*SR)zR
zufuc#lf&NlRI(D)0DV0?+gtUBd0NKw|gz2?<3g
zo_vQu?wD&}^l801g=NEaycJ
zE^oI;=^L~wJFY@PT@N?sjOwBvzY5N8NM
zM>Uu%PL<+1*MJX>1|o;2LH$+aDxO(kjmM+nKqLgj8pU>~qX_iJW;xULxF4F5nJ&ke%Y2SIdLYEB=)OVk&=A=x!%cn-JFgZj7u80_8so_@W<(=
zULTg-*sVy<&=#45=&MHxIW5GK)btJc8%1#BPtXA~@e-@KW?!@S76-msmZuzrggs
zS?=ZlI${WIc3}J!Fdj`h7f46006?A(#+0Dp7;|o8>Urqn5PmAp2h3|j_B9hyYCR2Y
zsq#~bDoY;;&XEG(y+j>s`9{C0W63Pp)r=2INS`6eTmko!hk&!@Mqm+ofvIDV;EE92
zeEL(u0_R`ouG&oflW+u&fN;^kc{Hw=E&ulI#&2>i*QC|CrsvvSZP-JHDu4Nsom|cm
z-^{>?@RGhZ=N0$^^=5^%>)X|oU`tJlI!YXt@lw}Wjb2W3%lhsv=p*qq2+U&r9m%~+
z2FU#NVOAp{_>xr
zSmpqT`2n6Tf4WeJI-27#?hfUXe@1An!UpZcYnwJntA-`t=SeZj&e`&bSpVQytP;>1
zuTuoFa4f@rELe&tcX|*C%nDx!4H;lGCx~u+>aqOx>LMrTs5xl=&iIJML@6<|ofH8&WL@6?2c!u$xYFj88
zHS7F&E&>!3V)Y?&Hdow-_rh`Th#zYn5ki$%d)k*jSP`zK$&{7u1~YxLj6S8e3(GG{
z>MkGmovsI{X^Q)Y33ckueW+miDD8g*0h|C`s>ufsvh+`UcF(?s4p>cvW-}7X1Nf2T
z5NN%(-v#R!U(`gwEVdH=2-rRCf0O0;G?$E{-(J|M^f$pn)6~?U$iE0j#vUC$=bjzr
zcG1&PX#>hgt^DCHCypnpFW(W$P_Z~>V;mIOor_s-9d8)a6+oPI>_k`ktX8!X9HJHe
z(HwGL8?w?R)K8D&x9jLawak=+fR&c`8ejgI{5iDcK^{J?_MdU~c&XX=g4eC|axzYpj4tmvLG
zIVnS8Dvf$k$4y}Y0Tw_8RV?Uxfn}UZzHM)C=CcUZ>}n|of!SK&1grXZI9D?jC+3~9pS40=H(+P>Ni+|z=$^&Im!ghh<b?q>1i+R#IySzmT^Rq0hZ|k57MzKzbAAIHPp@UO65JmDjy3Ldv;7is>ATDA%gfvC$1Jx~?EK!wMd~nF
z@hMw_CWhbZ^;O%!n
zm5$tt;HNKyr|b9zdt|^$+EA5q(iWqc}_J)S8;I8o}U$o8B*(?+6UaU`=*h()YV
z5wqSvigC@f6-W1?T%N%*ws{iTz*-fXn*cAz>V&P`tysRL(_{32rI@(QzcD}%(u-A7
zI1(9*&|1|oC$kr&80Qh)?9@{YwSvBYwTI;Ss`>&h>SVMALhGI44661GAXJ_c?8!
zlVHQ}X;#tR%$TPmXWTMP+a~x$hB;hb
z1)6~Ok#PQ-@ypy7GPT26k{o+@%+Qf@Zkd+i5A=!BEtkw5PZObBW~!%#ZqLbIA&H}-
zUAtak`5ZGiA3*n;)GIR<-sc$MR2^%Vw-2$Y*XxxBU+7|z6IWZG9NMRLyr~QOD|u|;
zYoWbu2)C^R4+X*r;t(qWUrcr5{I;ZFCz!a_JmxE{?Ku|b@9(yMYV!6I-iJOj3E^FP?grPSN=mp@gVoJ~hgD=}XYfjbjPJs@tVBz`!o
zI0Wlf`F9g`qfF+q!cy!q3vRkTWVk%Gxu?Dq9Xh`tmS6X78V%IHoa9DIeFD&`KR`Xe
zA$*MjJ9`QS*!uLEP-AeERntJ+%|{~{IIOY6m_BQlf~6XTkXfs@`!^Om8ZpA>y^p)Q
z>A0=96r?gwhgx*w(a0G^^Nwy-yqVI@}`j=7d{$!hQ}I8
zlIgQz3i9|1sqo4a!2~|8JsMeSGkW1mPp@Mx!E5>~U6EiK+sR{L?8YS$3|-^8aW|*w
zH3r!l$}f8eOUhvh4r?Oup=+@B+1u
z&}f|nhvpLa@D&mJX89fVHquszds_rgFH9KZcDNIs`yTb2H(0#2^XDlyt^e(vg*z`i
z@usuBnV?tNqmf;i&z-~{2oji=Y`9Yx$^5~#N8F+RqCh0^k`o)ER?7En><$*7O+x6G
zzgGm~2W`iAQu!Wxi|!j|6I|Wgp~>`Ur}&&_$=)w*S7UJvlilO7O*=-rmw3`rp6-}^
ze+JgjN*r0dyuH&s;Ehboh}?Vv9_Y4K=HEeCTXeSzYZZn-MRd#aKv0+XpixZd+T?h&
zHM}_jamMVILM;FG_Gy#EHrM|(8@;bicaEQATHL6|yEW)8N`=#2LST%2e1mak1j@jK
z(3bg=BGe2^h2_Bin`@Qlg#IM%lu@TVr{4+`yf08W#jRl=bTcWd&i;arHok9E6Y4W$
z=zTjR!+VnqrSFTGn10bEF~*&fj7Dz4w^+hwx)=;w?oW49>Hwi`1J;M$T0_zX))xS>
z8Dyg{wpTnIcaLLas)}vC2mo8Qc>*0L&j#5-WBzu@XZ+wG%l+8yIt2y;cXPE~kf{Vm
zcMAtL8QZFm^x
zRj!8nU!INTD{(b92x2-6DhwFCNEai|B~uYygVw@~+MWF+U9k
zonKEUqBUO{(YCyL13jtf!8;ie8UXWWg5SeD$XD}418vKXXIRvxSxk(ElrQ;f
zzgs0!YgX*nGQwM$CWdw?Ol&a)tMCGPYB|r>kNMJ7`X6wVBkOnLXZLC`bm4@9EU3(&
zqUwheMOILruiu*oJK)7VeSaAjN!|5e1C;?4zv0<02z&w*_&8b8N<
z1LF>?QY8u;YC+x(?RWk%HI65ctazJB%9Iuay~m)skAic|x|~hj6P)eQYv>XPa=Ikk
zNN^MQ)3%f(v>~y;czlYNppJboK>OV+nT~-HkhgX;l(exAwj9@s1e*_C6qiFy4$eNu
zTg&(z=-+fOlytH|B71A>Rgb}Q2%o#1eb7iE;4Phc&qDDIgA4pxhzmB9umSQBASVEF
zQ~x(=CRZ0H`c?a#*?WBiZ07OM4$`PQ&qI*Mjw{X_*-M$jpo4%!UkbQWk+O%;n>VKJ
zACb1<9k*(~!?xNY7mpFq`{C*obZ_d1l)dA8P^njAV^LieE_l24yTTc16vVsw2z-Ty
znCdA$wL}!3Uc!?wf~ZV?Z#n(_<^LSmJcV>EW!My+p4tmrV90)?U7nuEa`;~jeic6g
R@3V?Or1f6wN?bz^{V()ZkB$HU
literal 0
HcmV?d00001
diff --git a/assets/green_leaf.png b/assets/green_leaf.png
new file mode 100644
index 0000000000000000000000000000000000000000..485924636ff3fd7e587ae7fb308f194c8be8e511
GIT binary patch
literal 1288
zcmV+j1^4=iP)>veb)zyRVqG^XzSjYo7O1QB1BXK6%=U;
zKB)bns6P~c`1+v|EG?DR&}3(J!)iehR1_7#DhSf}s0cRVYjbBdrUv2@AFa}=)Y?YQ
z?#Avtd$aekH#LO}+&{}XbG|ue=9}-Fb(~q7wL{xBeC8d$ajf%X=TNcU@g>J{1+Hbq
z4Xn701pXm~FZ)M2PIP9f8F1v|Di(Z=6#ihv3Hu{~*C!^N!PO}g9kl^-spz**bxX%RrzX0zaS#%l+u4ctwqJWa(_bl)t3*6t7Qi9Sy
z(sA@b1Kv~49Yx|kz=FR=?CvNltSJh1HO^;&yPE3(S05DcdBtl;L8shl!M|AH*@6$<
ztqPPC-v|Z^f08VoKW)H!%ehln;guFCvV#=XayfTK9Dl_Mo>vPtz)FQ_0bdYzDGPiv
z1r`3rf)8>zH#ezYR#;>C+mrbzka)|?@rICYE8by%
zHm3`e`+Z!J$r^&|tP}mVXRv!t+m6}Cn`<21lr{rK8aNqQUpaTSkrs_>6nyvUww#iW
z7uf~I3VTW5?yP_}8VSB>okG!RN5jKjA0<-Ixv2%`zWIYW?i?@;o
zptvn~?C#}h0wBd#t&MmgYEDtG%bKHU0tn=Panzfa)JIL@SiCOo(#UzE-aM;jeohm>
zo<=8WIwA`^ZAp%g<4Sb4bW!2}Na62BCx(@h%r6Qqu<4KmZjCdS%elFB-6Rfx6@P4W
zl2=%maDG7Jn=7W-**J4a7SFdeQJMe-EdY1Mol6zCv01Uk@7{%qdtvb8a5!-Qr1-Gq
zWNWN>{=!ph-8PT{?+e^u
znu#SSjp%lQe07&+5wiU7w%Z5k6NXnuH&kmIpk5R)~N74dyWYx
zZq0De-zb-J9hm?(JC;{`$QE`DJijBXJChHWhCPDCDE`gnnLZQnRUnGgYOz+wTuqUf
ze{zuEr!2VK$=d9zT|L)##^vUhCsrK6idUMynONZy^Y~k3F@dj1@og4-!Zbl=I?c#|
yI(SoTr7#&Wqm2Y_sXl$T@$x)tvo>qawAsJ*r5OAzpqD50mo3D5NH&P^4L5;RWFZ
zdEw~=5^a%(#)Le)kU}e@uu}3kDVrq3i;eOeGvuimyE;0z{{1@l{{QFP|DC(K-}=3|
zo!|F-&-tJ8JKu9Iq>)CuM|Yq%?Fe0g8Ne=}7WfT31bULfP~sK?=Ya1(#1Q9!4x|t=
zfQi6i;A;%g5fPh7ku|*%Xb31e;)y?c(EkPffNely0HJSy)4&?Tl6nGkX-naqfwjOF
zAJO$d8Bi$lmmAuuiD(l{2cE_e{S!D26!~n>3nK3akef{5eSkA@gf{`@z<_{!`FgNk
zjswXOKLL0dL-=Q49gyw7TPC=glkpXb_2-qsTY(c6Gj2jJ;FI9p5~6mNYN1m6BVb%e
zeQX!puYk-ji0A7XEyW+w)*vhl*4Dz?$FxLAM;kU+3$TLZ;F9R;V4@zT-XLD>1skYk
zuml(upzT7@-)j=UCd0B>V>S#-f|xB>p#bVBi~Thl@;+rj>!4o(Dy$t;w5o85swOvyA~EbB7BiGehCs)m25EFP!3B2
z=4KoAjWym0k^;+o>G2!%%mzWO!4p~Injl$bkvqq50NR}1BSh|F2mNdiBsBr^`s&lx
zn|s}|?^z=MhJ$_v8SL}y0C`m+N4eNBmVK8Cjy*V{+OQS}#%aIGuyoW5oGq>jj(M&F
zC@_@M2SEQAC$6kIbru^piaf*JL;dEAt%g}u-e#(&%_x15_qO>K&_4SuA7$(kLWW^o
zN8_fxYQ7b}w>sMHJU#G)ov1{Xp{~b^jShWe0#^jj`!KJp8T!&Ky^V}YNAXn#E_FK#
z9b*m8AJI6e2O5Q{SR(@*S~UhPCkyQdwJC4K5MLq0cLHwfDpskVUb1ynQa-6fTWwk8
zR6Gy+QLCFinN5#cs^qOU)$k>p00000NkvXXu0mjf@r=Np
literal 0
HcmV?d00001
diff --git a/data/default_smells_config.json b/data/default_smells_config.json
new file mode 100644
index 0000000..4c7e3df
--- /dev/null
+++ b/data/default_smells_config.json
@@ -0,0 +1,101 @@
+{
+ "use-a-generator": {
+ "message_id": "R1729",
+ "name": "Use A Generator (UGEN)",
+ "acronym": "UGEN",
+ "enabled": true,
+ "smell_description": "Using generators instead of lists reduces memory consumption and avoids unnecessary allocations, leading to more efficient CPU and energy use.",
+ "analyzer_options": {}
+ },
+ "too-many-arguments": {
+ "message_id": "R0913",
+ "name": "Too Many Arguments (LPL)",
+ "acronym": "LPL",
+ "enabled": true,
+ "smell_description": "Functions with many arguments are harder to optimize and often require more memory and call overhead, increasing CPU load and energy usage.",
+ "analyzer_options": {
+ "max_args": {
+ "label": "Number of Arguments",
+ "description": "Detecting functions with this many arguments.",
+ "value": 6
+ }
+ }
+ },
+ "no-self-use": {
+ "message_id": "R6301",
+ "name": "No Self Use (NSU)",
+ "acronym": "NSU",
+ "enabled": true,
+ "smell_description": "Methods that don't use 'self' can be static, reducing object overhead and avoiding unnecessary memory binding at runtime.",
+ "analyzer_options": {}
+ },
+ "long-lambda-expression": {
+ "message_id": "LLE001",
+ "name": "Long Lambda Expression (LLE)",
+ "acronym": "LLE",
+ "enabled": true,
+ "smell_description": "Complex lambdas are harder for the interpreter to optimize and may lead to repeated evaluations, which can increase CPU usage and energy draw.",
+ "analyzer_options": {
+ "threshold_length": {
+ "label": "Lambda Length",
+ "description": "Detects lambda expressions exceeding this length.",
+ "value": 9
+ },
+ "threshold_count": {
+ "label": "Repetition Count",
+ "description": "Flags patterns that repeat at least this many times.",
+ "value": 5
+ }
+ }
+ },
+ "long-message-chain": {
+ "message_id": "LMC001",
+ "name": "Long Message Chain (LMC)",
+ "acronym": "LMC",
+ "enabled": true,
+ "smell_description": "Deeply nested calls create performance bottlenecks due to increased dereferencing and lookup time, which adds to CPU cycles and energy usage.",
+ "analyzer_options": {
+ "threshold": {
+ "label": "Threshold",
+ "description": "Defines a threshold for triggering this smell.",
+ "value": 9
+ }
+ }
+ },
+ "long-element-chain": {
+ "message_id": "LEC001",
+ "name": "Long Element Chain (LEC)",
+ "acronym": "LEC",
+ "enabled": true,
+ "smell_description": "Chained element access can be inefficient in large structures, increasing access time and CPU effort, thereby consuming more energy.",
+ "analyzer_options": {
+ "threshold": {
+ "label": "Threshold",
+ "description": "Defines a threshold for triggering this smell.",
+ "value": 3
+ }
+ }
+ },
+ "cached-repeated-calls": {
+ "message_id": "CRC001",
+ "name": "Cached Repeated Calls (CRC)",
+ "acronym": "CRC",
+ "enabled": true,
+ "smell_description": "Failing to cache repeated expensive calls leads to redundant computation, which wastes CPU cycles and drains energy needlessly.",
+ "analyzer_options": {
+ "threshold": {
+ "label": "Cache Threshold",
+ "description": "Number of times a function must repeat before caching.",
+ "value": 2
+ }
+ }
+ },
+ "string-concat-loop": {
+ "message_id": "SCL001",
+ "name": "String Concatenation in Loops (SCL)",
+ "acronym": "SCL",
+ "enabled": true,
+ "smell_description": "String concatenation in loops creates new objects each time, increasing memory churn and CPU workload, which leads to higher energy consumption.",
+ "analyzer_options": {}
+ }
+}
diff --git a/data/working_smells_config.json b/data/working_smells_config.json
new file mode 100644
index 0000000..4c7e3df
--- /dev/null
+++ b/data/working_smells_config.json
@@ -0,0 +1,101 @@
+{
+ "use-a-generator": {
+ "message_id": "R1729",
+ "name": "Use A Generator (UGEN)",
+ "acronym": "UGEN",
+ "enabled": true,
+ "smell_description": "Using generators instead of lists reduces memory consumption and avoids unnecessary allocations, leading to more efficient CPU and energy use.",
+ "analyzer_options": {}
+ },
+ "too-many-arguments": {
+ "message_id": "R0913",
+ "name": "Too Many Arguments (LPL)",
+ "acronym": "LPL",
+ "enabled": true,
+ "smell_description": "Functions with many arguments are harder to optimize and often require more memory and call overhead, increasing CPU load and energy usage.",
+ "analyzer_options": {
+ "max_args": {
+ "label": "Number of Arguments",
+ "description": "Detecting functions with this many arguments.",
+ "value": 6
+ }
+ }
+ },
+ "no-self-use": {
+ "message_id": "R6301",
+ "name": "No Self Use (NSU)",
+ "acronym": "NSU",
+ "enabled": true,
+ "smell_description": "Methods that don't use 'self' can be static, reducing object overhead and avoiding unnecessary memory binding at runtime.",
+ "analyzer_options": {}
+ },
+ "long-lambda-expression": {
+ "message_id": "LLE001",
+ "name": "Long Lambda Expression (LLE)",
+ "acronym": "LLE",
+ "enabled": true,
+ "smell_description": "Complex lambdas are harder for the interpreter to optimize and may lead to repeated evaluations, which can increase CPU usage and energy draw.",
+ "analyzer_options": {
+ "threshold_length": {
+ "label": "Lambda Length",
+ "description": "Detects lambda expressions exceeding this length.",
+ "value": 9
+ },
+ "threshold_count": {
+ "label": "Repetition Count",
+ "description": "Flags patterns that repeat at least this many times.",
+ "value": 5
+ }
+ }
+ },
+ "long-message-chain": {
+ "message_id": "LMC001",
+ "name": "Long Message Chain (LMC)",
+ "acronym": "LMC",
+ "enabled": true,
+ "smell_description": "Deeply nested calls create performance bottlenecks due to increased dereferencing and lookup time, which adds to CPU cycles and energy usage.",
+ "analyzer_options": {
+ "threshold": {
+ "label": "Threshold",
+ "description": "Defines a threshold for triggering this smell.",
+ "value": 9
+ }
+ }
+ },
+ "long-element-chain": {
+ "message_id": "LEC001",
+ "name": "Long Element Chain (LEC)",
+ "acronym": "LEC",
+ "enabled": true,
+ "smell_description": "Chained element access can be inefficient in large structures, increasing access time and CPU effort, thereby consuming more energy.",
+ "analyzer_options": {
+ "threshold": {
+ "label": "Threshold",
+ "description": "Defines a threshold for triggering this smell.",
+ "value": 3
+ }
+ }
+ },
+ "cached-repeated-calls": {
+ "message_id": "CRC001",
+ "name": "Cached Repeated Calls (CRC)",
+ "acronym": "CRC",
+ "enabled": true,
+ "smell_description": "Failing to cache repeated expensive calls leads to redundant computation, which wastes CPU cycles and drains energy needlessly.",
+ "analyzer_options": {
+ "threshold": {
+ "label": "Cache Threshold",
+ "description": "Number of times a function must repeat before caching.",
+ "value": 2
+ }
+ }
+ },
+ "string-concat-loop": {
+ "message_id": "SCL001",
+ "name": "String Concatenation in Loops (SCL)",
+ "acronym": "SCL",
+ "enabled": true,
+ "smell_description": "String concatenation in loops creates new objects each time, increasing memory churn and CPU workload, which leads to higher energy consumption.",
+ "analyzer_options": {}
+ }
+}
diff --git a/media/script.js b/media/script.js
deleted file mode 100644
index ae26b65..0000000
--- a/media/script.js
+++ /dev/null
@@ -1,122 +0,0 @@
-const vscode = acquireVsCodeApi();
-
-function updateWebView(data, sep) {
- // Hide "No refactoring in progress" message
- document.getElementById('no-data').style.display = 'none';
- document.getElementById('container').style.display = 'block';
-
- // Update Energy Saved
- document.getElementById(
- 'energy'
- ).textContent = `Carbon Saved: ${data.energySaved.toExponential(3)} kg CO2`;
-
- // Populate Target File
- const targetFile = data.targetFile;
- const targetFileList = document.getElementById('target-file-list');
- targetFileList.innerHTML = '';
- const li = document.createElement('li');
-
- const relFile = findRelPath(targetFile.refactored, sep);
- if (relFile.length === 0) {
- relFile = targetFile.original;
- }
- li.textContent = relFile;
-
- li.classList.add('clickable');
- li.onclick = () => {
- vscode.postMessage({
- command: 'selectFile',
- original: targetFile.original,
- refactored: targetFile.refactored
- });
- };
- targetFileList.appendChild(li);
-
- // Populate Other Modified Files
- const affectedFileList = document.getElementById('affected-file-list');
- affectedFileList.innerHTML = '';
- if (data.affectedFiles.length === 0) {
- document.getElementById('other-files-head').style.display = 'none';
- }
- data.affectedFiles.forEach((file) => {
- const li = document.createElement('li');
- const relFile = findRelPath(file.refactored, sep);
-
- if (relFile.length === 0) {
- relFile = file.original;
- }
-
- li.textContent = relFile;
- li.classList.add('clickable');
- li.onclick = () => {
- vscode.postMessage({
- command: 'selectFile',
- original: file.original,
- refactored: file.refactored
- });
- };
- affectedFileList.appendChild(li);
- });
-
- // Save state in the webview
- vscode.setState(data);
-}
-
-// Function to clear the UI (for when refactoring is done)
-function clearWebview() {
- document.getElementById('energy').textContent = 'Carbon Saved: --';
- document.getElementById('target-file-list').innerHTML = '';
- document.getElementById('affected-file-list').innerHTML = '';
-
- document.getElementById('no-data').style.display = 'block';
- document.getElementById('container').style.display = 'none';
- vscode.setState(null); // Clear state
-}
-
-// Restore state when webview loads
-window.addEventListener('DOMContentLoaded', () => {
- const savedState = vscode.getState();
- if (savedState) {
- updateWebView(savedState);
- }
-});
-
-// Listen for extension messages
-window.addEventListener('message', (event) => {
- if (event.data.command === 'update') {
- updateWebView(event.data.data, event.data.sep);
- } else if (event.data.command === 'clear') {
- clearWebview();
- } else if (event.data.command === 'pause') {
- document.getElementById('no-data').style.display = 'block';
- document.getElementById('container').style.display = 'none';
- }
-});
-
-// Button click handlers
-document.getElementById('accept-btn').addEventListener('click', () => {
- vscode.postMessage({ command: 'accept' });
- clearWebview();
-});
-
-document.getElementById('reject-btn').addEventListener('click', () => {
- vscode.postMessage({ command: 'reject' });
- clearWebview();
-});
-
-function findRelPath(filePath, sep) {
- // Split the path using the separator
- const parts = filePath.split(sep);
-
- // Find the index of the part containing the 'ecooptimizer-' substring
- const index = parts.findIndex((part) => part.includes('ecooptimizer-'));
-
- // If a matching part is found, return the joined list of items after it
- if (index !== -1) {
- // Slice the array from the next index and join them with the separator
- return parts.slice(index + 1).join(sep);
- }
-
- // Return an empty string if no match is found
- return '';
-}
diff --git a/media/style.css b/media/style.css
deleted file mode 100644
index 2ce8d4e..0000000
--- a/media/style.css
+++ /dev/null
@@ -1,42 +0,0 @@
-body {
- font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans',
- Arial, sans-serif;
- padding: 10px;
-}
-
-/* .body-text {
- font-size: medium;
-} */
-.clickable {
- cursor: pointer;
- padding: 5px;
- transition: background-color 0.2s ease-in-out;
-}
-.clickable:hover {
- background-color: rgba(87, 82, 82, 0.1);
-}
-#container {
- display: none; /* Initially hidden until data is received */
-}
-#no-data {
- /* font-size: 14px; */
- text-align: center;
-}
-#buttons {
- position: absolute;
- display: flex;
- justify-content: space-between;
- margin: 0 5px;
- bottom: 10px;
-}
-ul {
- overflow-y: auto;
- padding: 0;
- list-style-type: square;
-}
-
-button {
- width: 45vw;
- height: 40px;
- border-radius: 2px;
-}
diff --git a/media/vscode.css b/media/vscode.css
deleted file mode 100644
index 12d43b9..0000000
--- a/media/vscode.css
+++ /dev/null
@@ -1,91 +0,0 @@
-:root {
- --container-paddding: 20px;
- --input-padding-vertical: 6px;
- --input-padding-horizontal: 4px;
- --input-margin-vertical: 4px;
- --input-margin-horizontal: 0;
-}
-
-body {
- padding: 0 var(--container-paddding);
- color: var(--vscode-foreground);
- font-size: var(--vscode-font-size);
- font-weight: var(--vscode-font-weight);
- font-family: var(--vscode-font-family);
- background-color: var(--vscode-editor-background);
-}
-
-ol,
-ul {
- padding-left: var(--container-paddding);
-}
-
-body > *,
-form > * {
- margin-block-start: var(--input-margin-vertical);
- margin-block-end: var(--input-margin-vertical);
-}
-
-*:focus {
- outline-color: var(--vscode-focusBorder) !important;
-}
-
-a {
- color: var(--vscode-textLink-foreground);
-}
-
-a:hover,
-a:active {
- color: var(--vscode-textLink-activeForeground);
-}
-
-code {
- font-size: var(--vscode-editor-font-size);
- font-family: var(--vscode-editor-font-family);
-}
-
-button {
- border: none;
- padding: var(--input-padding-vertical) var(--input-padding-horizontal);
- width: 100%;
- text-align: center;
- outline: 1px solid transparent;
- outline-offset: 2px !important;
- color: var(--vscode-button-foreground);
- background: var(--vscode-button-background);
-}
-
-button:hover {
- cursor: pointer;
- background: var(--vscode-button-hoverBackground);
-}
-
-button:focus {
- outline-color: var(--vscode-focusBorder);
-}
-
-button.secondary {
- color: var(--vscode-button-secondaryForeground);
- background: var(--vscode-button-secondaryBackground);
-}
-
-button.secondary:hover {
- background: var(--vscode-button-secondaryHoverBackground);
-}
-
-input:not([type='checkbox']),
-textarea {
- display: block;
- width: 100%;
- border: none;
- font-family: var(--vscode-font-family);
- padding: var(--input-padding-vertical) var(--input-padding-horizontal);
- color: var(--vscode-input-foreground);
- outline-color: var(--vscode-input-border);
- background-color: var(--vscode-input-background);
-}
-
-input::placeholder,
-textarea::placeholder {
- color: var(--vscode-input-placeholderForeground);
-}
diff --git a/media/webview.html b/media/webview.html
deleted file mode 100644
index 4a1b2f6..0000000
--- a/media/webview.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
- Refactoring Navigator
-
-
-
- Nothing to see here. If you are currently refactoring a file, make sure the diff view is selected.
-
-
-
Refactoring Summary
-
Carbon Saved: --
-
Target File
-
-
Other Modified Files
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 0688744..8dfc29b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "ecooptimizer",
- "version": "0.0.1",
+ "version": "0.2.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ecooptimizer",
- "version": "0.0.1",
+ "version": "0.2.2",
"dependencies": {
"@types/dotenv": "^6.1.1",
"bufferutil": "^4.0.9",
@@ -16,6 +16,7 @@
"ws": "^8.18.0"
},
"devDependencies": {
+ "@types/adm-zip": "^0.5.7",
"@types/jest": "^29.5.14",
"@types/node": "20.x",
"@types/vscode": "^1.92.0",
@@ -239,13 +240,14 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.26.9",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz",
- "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==",
+ "version": "7.26.10",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz",
+ "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/template": "^7.26.9",
- "@babel/types": "^7.26.9"
+ "@babel/types": "^7.26.10"
},
"engines": {
"node": ">=6.9.0"
@@ -530,10 +532,11 @@
}
},
"node_modules/@babel/types": {
- "version": "7.26.9",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
- "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
+ "version": "7.26.10",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz",
+ "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
@@ -1520,6 +1523,15 @@
"optional": true,
"peer": true
},
+ "node_modules/@types/adm-zip": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz",
+ "integrity": "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
diff --git a/package.json b/package.json
index a338566..124eddb 100644
--- a/package.json
+++ b/package.json
@@ -1,15 +1,20 @@
{
"name": "ecooptimizer",
+ "publisher": "mac-ecooptimizers",
"displayName": "EcoOptimizer VS Code Plugin",
"contributors": [
"Sevhena Walker",
"Tanveer Brar",
"Ayushi Amin",
"Mya Hussain",
- "Nivetah Kuruparan"
+ "Nivetha Kuruparan"
],
"description": "VS Code Plugin for EcoOptimizer Refactoring Tool",
- "version": "0.0.1",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ssm-lab/capstone--sco-vs-code-plugin"
+ },
+ "version": "0.2.3",
"engines": {
"vscode": "^1.92.0"
},
@@ -17,249 +22,433 @@
"Other"
],
"activationEvents": [
- "onLanguage:python"
+ "onLanguage:python",
+ "onStartupFinished"
],
"main": "./dist/extension.js",
+ "directories": {
+ "src": "./src",
+ "test": "./test"
+ },
+ "scripts": {
+ "deploy": "vsce publish --yarn",
+ "vscode:prepublish": "npm run package",
+ "compile": "webpack",
+ "test": "jest --verbose",
+ "test:watch": "jest --watch --verbose",
+ "watch": "webpack --watch",
+ "package": "webpack --mode production --devtool hidden-source-map",
+ "lint": "eslint src"
+ },
+ "jest": {
+ "preset": "ts-jest",
+ "testEnvironment": "node",
+ "setupFilesAfterEnv": [
+ "./test/setup.ts"
+ ],
+ "moduleNameMapper": {
+ "^vscode$": "/test/mocks/vscode-mock.ts",
+ "^@/(.*)$": "/src/$1"
+ },
+ "moduleDirectories": [
+ "node_modules",
+ "src",
+ "test/__mocks__"
+ ],
+ "roots": [
+ "/src",
+ "/test"
+ ],
+ "collectCoverage": true,
+ "coverageReporters": [
+ "text",
+ "html",
+ "lcov"
+ ],
+ "coverageDirectory": "/coverage/",
+ "coverageThreshold": {
+ "global": {
+ "statements": 80
+ }
+ },
+ "collectCoverageFrom": [
+ "src/**/*.ts",
+ "!src/**/*.d.ts",
+ "!src/**/index.ts",
+ "!test/mocks/*",
+ "!src/extension.ts",
+ "!src/context/*",
+ "!src/providers/*",
+ "!src/commands/showLogs.ts",
+ "!src/emitters/serverStatus.ts",
+ "!src/utils/envConfig.ts",
+ "!src/utils/TreeStructureBuilder.ts",
+ "!src/commands/views/jumpToSmell.ts",
+ "!src/commands/views/openFile.ts",
+ "!src/lib/*",
+ "!src/install.ts"
+ ]
+ },
+ "lint-staged": {
+ "src/**/*.ts": [
+ "eslint --fix",
+ "prettier --write"
+ ]
+ },
+ "devDependencies": {
+ "@types/adm-zip": "^0.5.7",
+ "@types/jest": "^29.5.14",
+ "@types/node": "20.x",
+ "@types/vscode": "^1.92.0",
+ "@types/ws": "^8.5.14",
+ "@typescript-eslint/eslint-plugin": "^8.24.1",
+ "@typescript-eslint/parser": "^8.24.1",
+ "@vscode/test-cli": "^0.0.10",
+ "@vscode/test-electron": "^2.4.1",
+ "css-loader": "^7.1.2",
+ "eslint": "^9.21.0",
+ "eslint-config-prettier": "^10.0.1",
+ "eslint-plugin-prettier": "^5.2.3",
+ "eslint-plugin-unused-imports": "^4.1.4",
+ "husky": "^9.1.7",
+ "jest": "^29.7.0",
+ "jest-silent-reporter": "^0.6.0",
+ "lint-staged": "^15.4.3",
+ "prettier": "^3.5.2",
+ "prettier-plugin-tailwindcss": "^0.6.11",
+ "style-loader": "^4.0.0",
+ "ts-jest": "^29.2.6",
+ "ts-loader": "^9.5.1",
+ "typescript": "^5.7.2",
+ "webpack": "^5.95.0",
+ "webpack-cli": "^5.1.4",
+ "webpack-node-externals": "^3.0.0"
+ },
+ "dependencies": {
+ "@types/dotenv": "^6.1.1",
+ "bufferutil": "^4.0.9",
+ "dotenv": "^16.4.7",
+ "dotenv-webpack": "^8.1.0",
+ "utf-8-validate": "^6.0.5",
+ "ws": "^8.18.0"
+ },
+ "icon": "./assets/eco_logo.png",
"contributes": {
+ "viewsContainers": {
+ "activitybar": [
+ {
+ "id": "ecooptimizer",
+ "title": "Eco",
+ "icon": "assets/eco-icon.png"
+ }
+ ]
+ },
+ "views": {
+ "ecooptimizer": [
+ {
+ "id": "ecooptimizer.refactorView",
+ "name": "Refactoring Details",
+ "icon": "assets/eco-icon.png"
+ },
+ {
+ "id": "ecooptimizer.smellsView",
+ "name": "Code Smells",
+ "icon": "assets/eco-icon.png"
+ },
+ {
+ "id": "ecooptimizer.metricsView",
+ "name": "Carbon Metrics",
+ "icon": "assets/eco-icon.png"
+ },
+ {
+ "id": "ecooptimizer.filterView",
+ "name": "Filter Smells",
+ "icon": "assets/eco-icon.png"
+ }
+ ]
+ },
+ "viewsWelcome": [
+ {
+ "view": "ecooptimizer.refactorView",
+ "contents": "Refactoring is currently not in progress. Try selecting a smell in the Code Smells view to start refactoring.",
+ "when": "!refactoringInProgress"
+ },
+ {
+ "view": "ecooptimizer.smellsView",
+ "contents": "No code smells detected yet. Configure your workspace to start analysis.\n\n[Configure Workspace](command:ecooptimizer.configureWorkspace)\n\n[Read the docs](https://code.visualstudio.com/api) to learn how to use Eco-Optimizer.",
+ "when": "!workspaceState.workspaceConfigured"
+ },
+ {
+ "view": "ecooptimizer.metricsView",
+ "contents": "No energy savings to declare. Configure your workspace to start saving energy!\n\n[Configure Workspace](command:ecooptimizer.configureWorkspace)\n\n[Read the docs](https://code.visualstudio.com/api) to learn how to use Eco-Optimizer.",
+ "when": "!workspaceState.workspaceConfigured"
+ }
+ ],
"commands": [
{
- "command": "ecooptimizer.detectSmells",
- "title": "Detect Smells",
+ "command": "ecooptimizer.startServer",
+ "title": "Start EcoOptimizer Server",
"category": "Eco"
},
{
- "command": "ecooptimizer.refactorSmell",
- "title": "Refactor Smell",
+ "command": "ecooptimizer.stopServer",
+ "title": "Stop EcoOptimizer Server",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.configureWorkspace",
+ "title": "Configure Workspace",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.resetConfiguration",
+ "title": "Reset Configuration",
"category": "Eco"
},
{
"command": "ecooptimizer.wipeWorkCache",
- "title": "Wipe Workspace Cache",
+ "title": "Clear Smells Cache",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.toggleSmellFilter",
+ "title": "Toggle Smell",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.editSmellFilterOption",
+ "title": "Edit Option",
+ "icon": "$(edit)",
"category": "Eco"
},
{
- "command": "ecooptimizer.showRefactorSidebar",
- "title": "Show Refactor Sidebar",
+ "command": "ecooptimizer.selectAllFilterSmells",
+ "title": "Select All Smells",
"category": "Eco"
},
{
- "command": "ecooptimizer.pauseRefactorSidebar",
- "title": "Pause Refactor Sidebar",
+ "command": "ecooptimizer.deselectAllFilterSmells",
+ "title": "Deselect All Smells",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.setFilterDefaults",
+ "title": "Set Filter Defaults",
"category": "Eco",
- "enablement": "false"
+ "when": "view == ecooptimizer.filterView && !refactoringInProgress"
+ },
+ {
+ "command": "ecooptimizer.detectSmellsFolder",
+ "title": "Detect Smells for All Files",
+ "icon": "$(search)",
+ "category": "Eco"
},
{
- "command": "ecooptimizer.clearRefactorSidebar",
- "title": "Clear Refactor Sidebar",
+ "command": "ecooptimizer.detectSmellsFile",
+ "title": "Detect Smells",
+ "icon": "$(search)",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.refactorAllSmellsOfType",
+ "title": "Refactor Smells By Type",
+ "icon": "$(tools)",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.refactorSmell",
+ "title": "Refactor Smell",
+ "icon": "$(tools)",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.acceptRefactoring",
+ "title": "Accept Refactoring",
"category": "Eco",
- "enablement": "false"
+ "icon": "$(check)"
},
{
- "command": "ecooptimizer.startLogging",
- "title": "Show Backend Logs",
+ "command": "ecooptimizer.rejectRefactoring",
+ "title": "Reject Refactoring",
"category": "Eco",
- "enablement": "false"
+ "icon": "$(close)"
},
{
- "command": "ecooptimizer.toggleSmellLinting",
- "title": "π Toggle Smell Linting",
+ "command": "ecooptimizer.exportMetricsData",
+ "title": "Export Metrics Data as JSON",
"category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.clearMetricsData",
+ "title": "Clear Metrics Data",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.metricsView.refresh",
+ "title": "Refresh Metrics Data",
+ "icon": "$(sync)",
+ "category": "Eco"
+ },
+ {
+ "command": "ecooptimizer.toggleSmellLintingOn",
+ "title": "Toggle Smell Linting",
+ "category": "Eco",
+ "icon": {
+ "light": "assets/darkgreen_leaf.png",
+ "dark": "assets/green_leaf.png"
+ }
+ },
+ {
+ "command": "ecooptimizer.toggleSmellLintingOff",
+ "title": "Toggle Smell Linting",
+ "category": "Eco",
+ "icon": {
+ "light": "assets/white_leaf.png",
+ "dark": "assets/black_leaf.png"
+ }
}
],
"menus": {
+ "view/title": [
+ {
+ "command": "ecooptimizer.resetConfiguration",
+ "when": "view == ecooptimizer.smellsView && workspaceState.workspaceConfigured && !refactoringInProgress",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.wipeWorkCache",
+ "when": "view == ecooptimizer.smellsView && workspaceState.workspaceConfigured && !refactoringInProgress",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.selectAllFilterSmells",
+ "when": "view == ecooptimizer.filterView && !refactoringInProgress",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.deselectAllFilterSmells",
+ "when": "view == ecooptimizer.filterView && !refactoringInProgress",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.setFilterDefaults",
+ "when": "view == ecooptimizer.filterView && !refactoringInProgress",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.exportMetricsData",
+ "when": "view == ecooptimizer.metricsView",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.clearMetricsData",
+ "when": "view == ecooptimizer.metricsView",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.metricsView.refresh",
+ "when": "view == ecooptimizer.metricsView",
+ "group": "navigation"
+ }
+ ],
+ "view/item/context": [
+ {
+ "command": "ecooptimizer.editSmellFilterOption",
+ "when": "viewItem == smellOption && !refactoringInProgress",
+ "group": "inline"
+ },
+ {
+ "command": "ecooptimizer.detectSmellsFolder",
+ "when": "view == ecooptimizer.smellsView && viewItem == directory && !refactoringInProgress",
+ "group": "inline"
+ },
+ {
+ "command": "ecooptimizer.detectSmellsFile",
+ "when": "view == ecooptimizer.smellsView && (viewItem == file || viewItem == file_with_smells) && !refactoringInProgress",
+ "group": "inline"
+ },
+ {
+ "command": "ecooptimizer.refactorAllSmellsOfType",
+ "when": "view == ecooptimizer.smellsView && viewItem == file_with_smells && !refactoringInProgress",
+ "group": "inline"
+ },
+ {
+ "command": "ecooptimizer.refactorSmell",
+ "when": "view == ecooptimizer.smellsView && viewItem == smell && !refactoringInProgress",
+ "group": "inline"
+ }
+ ],
"editor/title": [
{
- "command": "ecooptimizer.toggleSmellLinting",
- "group": "navigation",
- "when": "eco.smellLintingEnabled == false",
- "icon": {
- "light": "off.svg",
- "dark": "off.svg"
- }
+ "command": "ecooptimizer.toggleSmellLintingOn",
+ "when": "workspaceState.workspaceConfigured && editorLangId == python && ecooptimizer.smellLintingEnabled",
+ "group": "navigation"
},
{
- "command": "ecooptimizer.toggleSmellLinting",
- "group": "navigation",
- "when": "eco.smellLintingEnabled == true",
- "icon": {
- "light": "on.svg",
- "dark": "on.svg"
- }
+ "command": "ecooptimizer.toggleSmellLintingOff",
+ "when": "workspaceState.workspaceConfigured && editorLangId == python && !ecooptimizer.smellLintingEnabled",
+ "group": "navigation"
}
]
},
"configuration": {
"title": "EcoOptimizer",
"properties": {
- "ecooptimizer.projectWorkspacePath": {
- "type": "string",
- "default": "",
- "description": "Path to the folder to be targeted, relative to current workspace. Defaults to the currently open folder in VS Code."
- },
- "ecooptimizer.logsOutputPath": {
- "type": "string",
- "default": "",
- "description": "Path to store log files and output reports. Defaults to a 'logs' folder inside the workspace."
- },
- "detection.smells": {
+ "ecooptimizer.detection.smellsColours": {
"order": 1,
"type": "object",
"additionalProperties": false,
- "description": "Configure which smells to detect and their highlight colours.",
+ "description": "Configure highlight colours for smells.",
"default": {
- "long-element-chain": {
- "enabled": true,
- "colour": "lightblue"
- },
- "too-many-arguments": {
- "enabled": true,
- "colour": "lightcoral"
- },
- "long-lambda-expression": {
- "enabled": true,
- "colour": "mediumpurple"
- },
- "long-message-chain": {
- "enabled": true,
- "colour": "lightpink"
- },
- "cached-repeated-calls": {
- "enabled": true,
- "colour": "lightgreen"
- },
- "string-concat-loop": {
- "enabled": true,
- "colour": "lightsalmon"
- },
- "no-self-use": {
- "enabled": true,
- "colour": "lightcyan"
- },
- "use-a-generator": {
- "enabled": true,
- "colour": "yellow"
- }
+ "long-element-chain": "lightblue",
+ "too-many-arguments": "lightcoral",
+ "long-lambda-expression": "mediumpurple",
+ "long-message-chain": "lightpink",
+ "cached-repeated-calls": "lightgreen",
+ "string-concat-loop": "lightsalmon",
+ "no-self-use": "lightcyan",
+ "use-a-generator": "yellow"
},
"properties": {
"long-element-chain": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "default": true,
- "description": "Enable detection of long element chains."
- },
- "colour": {
- "type": "string",
- "default": "lightblue",
- "description": "Colour (css syntax) for highlighting long element chains."
- }
- }
+ "type": "string",
+ "default": "lightblue",
+ "description": "Colour (css syntax) for highlighting long element chains."
},
"too-many-arguments": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "default": true,
- "description": "Enable detection of functions with too many arguments."
- },
- "colour": {
- "type": "string",
- "default": "lightcoral",
- "description": "Colour (css syntax) for highlighting functions with too many arguments."
- }
- }
+ "type": "string",
+ "default": "lightcoral",
+ "description": "Colour (css syntax) for highlighting functions with too many arguments."
},
"long-lambda-expression": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "default": true,
- "description": "Enable detection of long lambda expressions."
- },
- "colour": {
- "type": "string",
- "default": "mediumpurple",
- "description": "Colour (css syntax) for highlighting long lambda expressions."
- }
- }
+ "type": "string",
+ "default": "mediumpurple",
+ "description": "Colour (css syntax) for highlighting long lambda expressions."
},
"long-message-chain": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "default": true,
- "description": "Enable detection of long message chains."
- },
- "colour": {
- "type": "string",
- "default": "lightpink",
- "description": "Colour (css syntax) for highlighting long message chains."
- }
- }
+ "type": "string",
+ "default": "lightpink",
+ "description": "Colour (css syntax) for highlighting long message chains."
},
"cached-repeated-calls": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "default": true,
- "description": "Enable detection of cached repeated calls."
- },
- "colour": {
- "type": "string",
- "default": "lightgreen",
- "description": "Colour (css syntax) for highlighting cached repeated calls."
- }
- }
+ "type": "string",
+ "default": "lightgreen",
+ "description": "Colour (css syntax) for highlighting cached repeated calls."
},
"string-concat-loop": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "default": true,
- "description": "Enable detection of string concatenation in loops."
- },
- "colour": {
- "type": "string",
- "default": "lightsalmon",
- "description": "Colour (css syntax) for highlighting string concatenation in loops."
- }
- }
+ "type": "string",
+ "default": "lightsalmon",
+ "description": "Colour (css syntax) for highlighting string concatenation in loops."
},
"no-self-use": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "default": true,
- "description": "Enable detection of methods with no self-use."
- },
- "colour": {
- "type": "string",
- "default": "lightcyan",
- "description": "Colour (css syntax) for highlighting methods with no self-use."
- }
- }
+ "type": "string",
+ "default": "lightcyan",
+ "description": "Colour (css syntax) for highlighting methods with no self-use."
},
"use-a-generator": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "default": true,
- "description": "Enable detection of places where a generator could be used."
- },
- "colour": {
- "type": "string",
- "default": "yellow",
- "description": "Colour (css syntax) for highlighting places where a generator could be used."
- }
- }
+ "type": "string",
+ "default": "yellow",
+ "description": "Colour (css syntax) for highlighting places where a generator could be used."
}
}
},
@@ -292,131 +481,6 @@
"description": "Choose a highlight style for all smells."
}
}
- },
- "keybindings": [
- {
- "command": "ecooptimizer.refactorSmell",
- "key": "ctrl+shift+r",
- "when": "editorTextFocus && resourceExtname == '.py'"
- }
- ],
- "viewsContainers": {
- "activitybar": [
- {
- "id": "refactorSidebarContainer",
- "title": "Refactoring",
- "icon": "resources/refactor-icon.svg"
- }
- ]
- },
- "views": {
- "refactorSidebarContainer": [
- {
- "id": "extension.refactorSidebar",
- "name": "Refactoring Summary",
- "type": "webview"
- }
- ]
}
- },
- "directories": {
- "src": "./src",
- "test": "./test"
- },
- "scripts": {
- "vscode:prepublish": "npm run package",
- "compile": "webpack",
- "test": "jest --verbose",
- "test:watch": "jest --watch --silent --verbose",
- "watch": "webpack --watch",
- "package": "webpack --mode production --devtool hidden-source-map",
- "compile-tests": "tsc -p . --outDir out",
- "watch-tests": "tsc -p . -w --outDir out",
- "lint": "eslint src",
- "prepare": "husky"
- },
- "jest": {
- "preset": "ts-jest",
- "testEnvironment": "node",
- "setupFilesAfterEnv": [
- "./test/setup.ts"
- ],
- "moduleNameMapper": {
- "^vscode$": "/test/mocks/vscode-mock.ts"
- },
- "moduleDirectories": [
- "node_modules",
- "tests/__mocks__"
- ],
- "roots": [
- "/src",
- "/test"
- ],
- "collectCoverage": true,
- "coverageReporters": [
- "text",
- "html",
- "lcov"
- ],
- "coverageDirectory": "/coverage/",
- "coverageThreshold": {
- "global": {
- "statements": 80
- }
- },
- "collectCoverageFrom": [
- "src/**/*.ts",
- "!src/**/*.d.ts",
- "!src/**/index.ts",
- "!test/mocks/*",
- "!src/extension.ts",
- "!src/context/*",
- "!src/utils/configManager.ts",
- "!src/commands/showLogs.ts",
- "!src/ui/refactorView.ts",
- "!src/utils/handleEditorChange.ts"
- ]
- },
- "lint-staged": {
- "src/**/*.ts": [
- "eslint --fix",
- "prettier --write"
- ]
- },
- "devDependencies": {
- "@types/jest": "^29.5.14",
- "@types/node": "20.x",
- "@types/vscode": "^1.92.0",
- "@types/ws": "^8.5.14",
- "@typescript-eslint/eslint-plugin": "^8.24.1",
- "@typescript-eslint/parser": "^8.24.1",
- "@vscode/test-cli": "^0.0.10",
- "@vscode/test-electron": "^2.4.1",
- "css-loader": "^7.1.2",
- "eslint": "^9.21.0",
- "eslint-config-prettier": "^10.0.1",
- "eslint-plugin-prettier": "^5.2.3",
- "eslint-plugin-unused-imports": "^4.1.4",
- "husky": "^9.1.7",
- "jest": "^29.7.0",
- "jest-silent-reporter": "^0.6.0",
- "lint-staged": "^15.4.3",
- "prettier": "^3.5.2",
- "prettier-plugin-tailwindcss": "^0.6.11",
- "style-loader": "^4.0.0",
- "ts-jest": "^29.2.6",
- "ts-loader": "^9.5.1",
- "typescript": "^5.7.2",
- "webpack": "^5.95.0",
- "webpack-cli": "^5.1.4",
- "webpack-node-externals": "^3.0.0"
- },
- "dependencies": {
- "@types/dotenv": "^6.1.1",
- "bufferutil": "^4.0.9",
- "dotenv": "^16.4.7",
- "dotenv-webpack": "^8.1.0",
- "utf-8-validate": "^6.0.5",
- "ws": "^8.18.0"
}
}
diff --git a/src/api/backend.ts b/src/api/backend.ts
index d5979ea..00a8e23 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,24 +1,51 @@
-import * as vscode from 'vscode';
-
+import {basename} from 'path';
import { envConfig } from '../utils/envConfig';
-import { serverStatus } from '../utils/serverStatus';
-import { ServerStatusType } from '../utils/serverStatus';
+import { serverStatus } from '../emitters/serverStatus';
+import { ServerStatusType } from '../emitters/serverStatus';
+import { ecoOutput } from '../extension';
-const BASE_URL = `http://${envConfig.SERVER_URL}`; // API URL for Python backend
+// Base URL for backend API endpoints constructed from environment configuration
+const BASE_URL = `http://${envConfig.SERVER_URL}`;
+/**
+ * Verifies backend service availability and updates extension status.
+ * Performs health check by hitting the /health endpoint and handles three scenarios:
+ * 1. Successful response (200-299) - marks server as UP
+ * 2. Error response - marks server as DOWN with status code
+ * 3. Network failure - marks server as DOWN with error details
+ */
export async function checkServerStatus(): Promise {
try {
- const response = await fetch('http://localhost:8000/health');
+ ecoOutput.info('[backend.ts] Checking backend server health status...');
+ const response = await fetch(`${BASE_URL}/health`);
+
if (response.ok) {
serverStatus.setStatus(ServerStatusType.UP);
+ ecoOutput.trace('[backend.ts] Backend server is healthy');
} else {
serverStatus.setStatus(ServerStatusType.DOWN);
+ ecoOutput.warn(`[backend.ts] Backend server unhealthy status: ${response.status}`);
}
- } catch {
+ } catch (error) {
serverStatus.setStatus(ServerStatusType.DOWN);
+ ecoOutput.error(
+ `[backend.ts] Server connection failed: ${error instanceof Error ? error.message : 'Unknown error'}`
+ );
}
}
+/**
+ * Initializes and synchronizes logs with the backend server.
+ *
+ * This function sends a POST request to the backend to initialize logging
+ * for the specified log directory. If the request is successful, logging
+ * is initialized; otherwise, an error is logged, and an error message is
+ * displayed to the user.
+ *
+ * @param log_dir - The directory path where logs are stored.
+ * @returns A promise that resolves to `true` if logging is successfully initialized,
+ * or `false` if an error occurs.
+ */
export async function initLogs(log_dir: string): Promise {
const url = `${BASE_URL}/logs/init`;
@@ -35,6 +62,9 @@ export async function initLogs(log_dir: string): Promise {
if (!response.ok) {
console.error(`Unable to initialize logging: ${JSON.stringify(response)}`);
+ ecoOutput.error(
+ `Unable to initialize logging: ${JSON.stringify(response)}`,
+ );
return false;
}
@@ -42,89 +72,177 @@ export async function initLogs(log_dir: string): Promise {
return true;
} catch (error: any) {
console.error(`Eco: Unable to initialize logging: ${error.message}`);
- vscode.window.showErrorMessage(
+ ecoOutput.error(
'Eco: Unable to reach the backend. Please check your connection.',
);
return false;
}
}
-// β
Fetch detected smells for a given file (only enabled smells)
+/**
+ * Analyzes source code for code smells using backend detection service.
+ * @param filePath - Absolute path to the source file for analysis
+ * @param enabledSmells - Configuration object specifying which smells to detect
+ * @returns Promise resolving to smell detection results and HTTP status
+ * @throws Error when:
+ * - Network request fails
+ * - Backend returns non-OK status
+ * - Response contains invalid data format
+ */
export async function fetchSmells(
filePath: string,
- enabledSmells: string[],
-): Promise {
+ enabledSmells: Record>,
+): Promise<{ smells: Smell[]; status: number }> {
const url = `${BASE_URL}/smells`;
+ const fileName = basename(filePath);
+ ecoOutput.info(`[backend.ts] Starting smell detection for: ${fileName}`);
try {
- console.log(
- `Eco: Requesting smells for file: ${filePath} with filters: ${enabledSmells}`,
- );
+ ecoOutput.debug(`[backend.ts] Request payload for ${fileName}:`, {
+ file_path: filePath,
+ enabled_smells: enabledSmells
+ });
const response = await fetch(url, {
- method: 'POST', // β
Send enabled smells in the request body
+ method: 'POST',
headers: {
'Content-Type': 'application/json',
},
- body: JSON.stringify({ file_path: filePath, enabled_smells: enabledSmells }), // β
Include enabled smells
+ body: JSON.stringify({
+ file_path: filePath,
+ enabled_smells: enabledSmells,
+ }),
});
if (!response.ok) {
- console.error(
- `Eco: API request failed (${response.status} - ${response.statusText})`,
- );
- vscode.window.showErrorMessage(
- `Eco: Failed to fetch smells`,
- );
- return [];
+ const errorMsg = `Backend request failed (${response.status})`;
+ ecoOutput.error(`[backend.ts] ${errorMsg}`);
+ try {
+ const errorBody = await response.json();
+ ecoOutput.error(`[backend.ts] Backend error details:`, errorBody);
+ } catch (e: any) {
+ ecoOutput.error(`[backend.ts] Could not parse error response`);
+ }
+ throw new Error(errorMsg);
}
- const smellsList = (await response.json()) as Smell[];
-
- if (!Array.isArray(smellsList)) {
- console.error('Eco: Invalid response format from backend.');
- vscode.window.showErrorMessage('Eco: Failed to fetch smells');
- return [];
+ const smellsList = await response.json();
+
+ // Detailed logging of the response
+ ecoOutput.info(`[backend.ts] Detection complete for ${fileName}`);
+ ecoOutput.debug(`[backend.ts] Raw response headers for ${fileName}:`, Object.fromEntries(response.headers.entries()));
+ ecoOutput.debug(`[backend.ts] Full response for ${fileName}:`, {
+ status: response.status,
+ statusText: response.statusText,
+ body: smellsList
+ });
+
+ // Detailed smell listing
+ ecoOutput.info(`[backend.ts] Detected ${smellsList.length} smells in ${fileName}`);
+ if (smellsList.length > 0) {
+ ecoOutput.debug(`[backend.ts] Complete smells list for ${fileName}:`, smellsList);
+ ecoOutput.debug(`[backend.ts] Verbose smell details for ${fileName}:`,
+ smellsList.map((smell: Smell) => ({
+ type: smell.symbol,
+ location: `${smell.path}:${smell.occurences}`,
+ message: smell.message,
+ context: smell.messageId
+ }))
+ );
}
- console.log(`Eco: Successfully retrieved ${smellsList.length} smells.`);
- return smellsList;
+ return { smells: smellsList, status: response.status };
+
} catch (error: any) {
- console.error(`Eco: Network error while fetching smells: ${error.message}`);
- vscode.window.showErrorMessage(
- 'Eco: Failed to fetch smells',
- );
- return [];
+ ecoOutput.error(`[backend.ts] Smell detection failed for ${fileName}: ${error.message}`);
+ if (error instanceof Error && error.stack) {
+ ecoOutput.trace(`[backend.ts] Error stack info:`, error.stack);
+ }
+ throw new Error(`Detection failed: ${error.message}`);
}
}
-// Request refactoring for a specific smell
-export async function refactorSmell(
- filePath: string,
+/**
+ * Executes code refactoring for a specific detected smell pattern.
+ * @param smell - The smell object containing detection details
+ * @param workspacePath - The path to the workspace.
+ * @returns Promise resolving to refactoring result data
+ * @throws Error when:
+ * - Workspace path is not provided
+ * - Refactoring request fails
+ * - Network errors occur
+ */
+export async function backendRefactorSmell(
smell: Smell,
-): Promise {
+ workspacePath: string,
+): Promise {
const url = `${BASE_URL}/refactor`;
- const workspaceFolder = vscode.workspace.workspaceFolders?.find((folder) =>
- filePath.includes(folder.uri.fsPath),
- )
+ // Validate workspace configuration
+ if (!workspacePath) {
+ ecoOutput.error('[backend.ts] Refactoring aborted: No workspace path');
+ throw new Error('No workspace path provided');
+ }
- if (!workspaceFolder) {
- console.error('Eco: Error - Unable to determine workspace folder for', filePath);
- throw new Error(
- `Eco: Unable to find a matching workspace folder for file: ${filePath}`,
- );
+ ecoOutput.info(`[backend.ts] Starting refactoring for smell: ${smell.symbol}`);
+ console.log('Starting refactoring for smell:', smell);
+
+ try {
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ sourceDir: workspacePath,
+ smell,
+ }),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ ecoOutput.error(`[backend.ts] Refactoring failed: ${errorData.detail || 'Unknown error'}`);
+ throw new Error(errorData.detail || 'Refactoring failed');
+ }
+
+ const result = await response.json();
+ ecoOutput.info(`[backend.ts] Refactoring successful for ${smell.symbol}`);
+ return result;
+
+ } catch (error: any) {
+ ecoOutput.error(`[backend.ts] Refactoring error: ${error.message}`);
+ throw new Error(`Refactoring failed: ${error.message}`);
}
+}
+
+/**
+ * Sends a request to the backend to refactor all smells of a type.
+ *
+ * @param smell - The smell to refactor.
+ * @param workspacePath - The path to the workspace.
+ * @returns A promise resolving to the refactored data or throwing an error if unsuccessful.
+ */
+export async function backendRefactorSmellType(
+ smell: Smell,
+ workspacePath: string
+): Promise {
+ const url = `${BASE_URL}/refactor-by-type`;
+ const filePath = smell.path;
+ const smellType = smell.symbol;
- const workspaceFolderPath = workspaceFolder.uri.fsPath;
+ // Validate workspace configuration
+ if (!workspacePath) {
+ ecoOutput.error('[backend.ts] Refactoring aborted: No workspace path');
+ throw new Error('No workspace path provided');
+ }
- console.log(
- `Eco: Initiating refactoring for smell "${smell.symbol}" in "${workspaceFolderPath}"`,
- );
+ ecoOutput.info(`[backend.ts] Starting refactoring for smells of type "${smellType}" in "${filePath}"`);
+ // Prepare the payload for the backend
const payload = {
- source_dir: workspaceFolderPath,
- smell,
+ sourceDir: workspacePath,
+ smellType,
+ firstSmell: smell,
};
try {
@@ -137,17 +255,17 @@ export async function refactorSmell(
});
if (!response.ok) {
- const errorText = await response.text();
- console.error(
- `Eco: Error - Refactoring smell "${smell.symbol}": ${errorText}`,
- );
- throw new Error(`Eco: Error refactoring smell: ${errorText}`);
+ const errorData = await response.json();
+ ecoOutput.error(`[backend.ts] Refactoring failed: ${errorData.detail || 'Unknown error'}`);
+ throw new Error(errorData.detail || 'Refactoring failed');
}
- const refactorResult = (await response.json()) as RefactorOutput;
- return refactorResult;
- } catch (error) {
- console.error('Eco: Unexpected error in refactorSmell:', error);
- throw error;
+ const result = await response.json();
+ ecoOutput.info(`[backend.ts] Refactoring successful for ${smell.symbol}`);
+ return result;
+
+ } catch (error: any) {
+ ecoOutput.error(`[backend.ts] Refactoring error: ${error.message}`);
+ throw new Error(`Refactoring failed: ${error.message}`);
}
-}
+}
\ No newline at end of file
diff --git a/src/commands/configureWorkspace.ts b/src/commands/configureWorkspace.ts
new file mode 100644
index 0000000..5e1901e
--- /dev/null
+++ b/src/commands/configureWorkspace.ts
@@ -0,0 +1,146 @@
+import * as vscode from 'vscode';
+import * as path from 'path';
+import * as fs from 'fs';
+import { envConfig } from '../utils/envConfig';
+
+/**
+ * Initializes workspace configuration by prompting user to select a Python project folder.
+ * This is the main entry point for workspace configuration and delegates to folder-specific logic.
+ *
+ * @param context - VS Code extension context containing workspace state management
+ */
+export async function configureWorkspace(
+ context: vscode.ExtensionContext,
+): Promise {
+ await configurePythonFolder(context);
+}
+
+/**
+ * Recursively identifies Python project folders by scanning for:
+ * - Python files (*.py)
+ * - \_\_init\_\_.py package markers
+ * Maintains a hierarchical understanding of Python projects in the workspace.
+ *
+ * @param folderPath - Absolute filesystem path to scan
+ * @returns Array of qualified Python project paths
+ */
+function findPythonFoldersRecursively(folderPath: string): string[] {
+ let pythonFolders: string[] = [];
+ let hasPythonFiles = false;
+
+ try {
+ const files = fs.readdirSync(folderPath);
+
+ // Validate current folder contains Python artifacts
+ if (
+ files.includes('__init__.py') ||
+ files.some((file) => file.endsWith('.py'))
+ ) {
+ hasPythonFiles = true;
+ }
+
+ // Recursively process subdirectories
+ for (const file of files) {
+ const filePath = path.join(folderPath, file);
+ if (fs.statSync(filePath).isDirectory()) {
+ const subfolderPythonFolders = findPythonFoldersRecursively(filePath);
+ if (subfolderPythonFolders.length > 0) {
+ hasPythonFiles = true;
+ pythonFolders.push(...subfolderPythonFolders);
+ }
+ }
+ }
+
+ // Include current folder if Python content found at any level
+ if (hasPythonFiles) {
+ pythonFolders.push(folderPath);
+ }
+ } catch (error) {
+ vscode.window.showErrorMessage(
+ `Workspace scanning error in ${path.basename(folderPath)}: ${(error as Error).message}`,
+ );
+ }
+
+ return pythonFolders;
+}
+
+/**
+ * Guides user through Python workspace selection process with validation.
+ * Presents filtered list of valid Python project folders and handles selection.
+ *
+ * @param context - Extension context for state persistence
+ */
+async function configurePythonFolder(
+ context: vscode.ExtensionContext,
+): Promise {
+ const workspaceFolders = vscode.workspace.workspaceFolders;
+
+ if (!workspaceFolders?.length) {
+ vscode.window.showErrorMessage(
+ 'No workspace detected. Please open a project folder first.',
+ );
+ return;
+ }
+
+ // Identify all Python project roots
+ const validPythonFolders = workspaceFolders
+ .map((folder) => folder.uri.fsPath)
+ .flatMap(findPythonFoldersRecursively);
+
+ if (validPythonFolders.length === 0) {
+ vscode.window.showErrorMessage(
+ 'No Python projects found. Workspace must contain .py files or __init__.py markers.',
+ );
+ return;
+ }
+
+ // Present interactive folder selection
+ const selectedFolder = await vscode.window.showQuickPick(
+ validPythonFolders.map((folder) => ({
+ label: path.basename(folder),
+ description: folder,
+ detail: `Python content: ${fs
+ .readdirSync(folder)
+ .filter((file) => file.endsWith('.py') || file === '__init__.py')
+ .join(', ')}`,
+ folderPath: folder,
+ })),
+ {
+ placeHolder: 'Select Python project root',
+ matchOnDescription: true,
+ matchOnDetail: true,
+ },
+ );
+
+ if (selectedFolder) {
+ await updateWorkspace(context, selectedFolder.folderPath);
+ vscode.window.showInformationMessage(
+ `Configured workspace: ${path.basename(selectedFolder.folderPath)}`,
+ );
+ }
+}
+
+/**
+ * Persists workspace configuration and updates extension context.
+ * Triggers view refreshes to reflect new workspace state.
+ *
+ * @param context - Extension context for state management
+ * @param workspacePath - Absolute path to selected workspace root
+ */
+export async function updateWorkspace(
+ context: vscode.ExtensionContext,
+ workspacePath: string,
+): Promise {
+ // Persist workspace path
+ await context.workspaceState.update(
+ envConfig.WORKSPACE_CONFIGURED_PATH!,
+ workspacePath,
+ );
+
+ // Update extension context for UI state management
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'workspaceState.workspaceConfigured',
+ true,
+ );
+}
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
deleted file mode 100644
index f597f96..0000000
--- a/src/commands/detectSmells.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import * as vscode from 'vscode';
-
-import { FileHighlighter } from '../ui/fileHighlighter';
-import { getEditorAndFilePath } from '../utils/editorUtils';
-import { fetchSmells } from '../api/backend';
-import { ContextManager } from '../context/contextManager';
-import { envConfig } from '../utils/envConfig';
-import { hashContent, updateHash } from '../utils/hashDocs';
-import { wipeWorkCache } from './wipeWorkCache';
-import { getEnabledSmells } from '../utils/handleSmellSettings';
-import { serverStatus, ServerStatusType } from '../utils/serverStatus';
-
-serverStatus.on('change', (newStatus: ServerStatusType) => {
- console.log('Server status changed:', newStatus);
- if (newStatus === ServerStatusType.DOWN) {
- vscode.window.showWarningMessage(
- 'Smell detection limited. Only cached smells will be shown.',
- );
- }
-});
-
-export interface SmellDetectRecord {
- hash: string;
- smells: Smell[];
-}
-
-let fileHighlighter: FileHighlighter;
-
-export async function detectSmells(contextManager: ContextManager): Promise {
- const { editor, filePath } = getEditorAndFilePath();
-
- // β
Ensure an active editor exists
- if (!editor) {
- vscode.window.showErrorMessage('Eco: No active editor found.');
- console.error('Eco: No active editor found to detect smells.');
- return;
- }
-
- // β
Ensure filePath is valid
- if (!filePath) {
- vscode.window.showErrorMessage('Eco: Active editor has no valid file path.');
- console.error('Eco: No valid file path found for smell detection.');
- return;
- }
-
- console.log(`Eco: Detecting smells in file: ${filePath}`);
-
- const enabledSmells = getEnabledSmells();
- if (!Object.values(enabledSmells).includes(true)) {
- vscode.window.showWarningMessage(
- 'Eco: No smells are enabled! Detection skipped.',
- );
- return;
- }
-
- // β
Check if the enabled smells have changed
- const lastUsedSmells = contextManager.getWorkspaceData(
- envConfig.LAST_USED_SMELLS_KEY!,
- {},
- );
- if (JSON.stringify(lastUsedSmells) !== JSON.stringify(enabledSmells)) {
- console.log('Eco: Smell settings have changed! Wiping cache.');
- await wipeWorkCache(contextManager, 'settings');
- contextManager.setWorkspaceData(envConfig.LAST_USED_SMELLS_KEY!, enabledSmells);
- }
-
- // Handle cache and previous smells
- const allSmells: Record =
- contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!) || {};
- const fileSmells = allSmells[filePath];
- const currentFileHash = hashContent(editor.document.getText());
-
- let smellsData: Smell[] | undefined;
-
- if (fileSmells && currentFileHash === fileSmells.hash) {
- vscode.window.showInformationMessage(`Eco: Using cached smells for ${filePath}`);
-
- smellsData = fileSmells.smells;
- } else if (serverStatus.getStatus() === ServerStatusType.UP) {
- updateHash(contextManager, editor.document);
-
- try {
- smellsData = await fetchSmells(
- filePath,
- Object.keys(enabledSmells).filter((s) => enabledSmells[s]),
- );
- } catch (err) {
- console.error(err);
- return;
- }
-
- if (smellsData) {
- allSmells[filePath] = { hash: currentFileHash, smells: smellsData };
- contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, allSmells);
- }
- } else {
- vscode.window.showWarningMessage(
- 'Action blocked: Server is down and no cached smells exist for this file version.',
- );
- return;
- }
-
- if (!smellsData || smellsData.length === 0) {
- vscode.window.showInformationMessage('Eco: No code smells detected.');
- return;
- }
-
- console.log(`Eco: Highlighting detected smells in ${filePath}.`);
- if (!fileHighlighter) {
- fileHighlighter = FileHighlighter.getInstance(contextManager);
- }
- fileHighlighter.highlightSmells(editor, smellsData);
-
- vscode.window.showInformationMessage(
- `Eco: Highlighted ${smellsData.length} smells.`,
- );
-
- // Set the linting state to enabled
- contextManager.setWorkspaceData(envConfig.SMELL_LINTING_ENABLED_KEY, true);
- vscode.commands.executeCommand('setContext', 'eco.smellLintingEnabled', true);
-}
diff --git a/src/commands/detection/detectSmells.ts b/src/commands/detection/detectSmells.ts
new file mode 100644
index 0000000..6dbb5a8
--- /dev/null
+++ b/src/commands/detection/detectSmells.ts
@@ -0,0 +1,193 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import * as path from 'path';
+
+import { fetchSmells } from '../../api/backend';
+import { SmellsViewProvider } from '../../providers/SmellsViewProvider';
+import { getEnabledSmells } from '../../utils/smellsData';
+import { serverStatus, ServerStatusType } from '../../emitters/serverStatus';
+import { SmellsCacheManager } from '../../context/SmellsCacheManager';
+import { ecoOutput } from '../../extension';
+
+/**
+ * Performs code smell analysis on a single Python file with comprehensive state management.
+ * Only shows user notifications for critical events requiring attention.
+ *
+ * @param filePath - Absolute path to the Python file to analyze
+ * @param smellsViewProvider - Provider for updating the UI with results
+ * @param smellsCacheManager - Manager for cached smell results
+ */
+export async function detectSmellsFile(
+ filePath: string,
+ smellsViewProvider: SmellsViewProvider,
+ smellsCacheManager: SmellsCacheManager,
+): Promise {
+ const shouldProceed = await precheckAndMarkQueued(
+ filePath,
+ smellsViewProvider,
+ smellsCacheManager,
+ );
+
+ if (!shouldProceed) return;
+
+ // Transform enabled smells into backend-compatible format
+ const enabledSmells = getEnabledSmells();
+ const enabledSmellsForBackend = Object.fromEntries(
+ Object.entries(enabledSmells).map(([key, value]) => [key, value.options]),
+ );
+
+ try {
+ ecoOutput.info(`[detection.ts] Analyzing: ${path.basename(filePath)}`);
+ const { smells, status } = await fetchSmells(filePath, enabledSmellsForBackend);
+
+ // Handle backend response
+ if (status === 200) {
+ if (smells.length > 0) {
+ ecoOutput.info(`[detection.ts] Detected ${smells.length} smells`);
+ smellsViewProvider.setStatus(filePath, 'passed');
+ await smellsCacheManager.setCachedSmells(filePath, smells);
+ smellsViewProvider.setSmells(filePath, smells);
+ } else {
+ ecoOutput.info('[detection.ts] File has no detectable smells');
+ smellsViewProvider.setStatus(filePath, 'no_issues');
+ await smellsCacheManager.setCachedSmells(filePath, []);
+ }
+ } else {
+ const msg = `Analysis failed for ${path.basename(filePath)} (status ${status})`;
+ ecoOutput.error(`[detection.ts] ${msg}`);
+ smellsViewProvider.setStatus(filePath, 'failed');
+ vscode.window.showErrorMessage(msg);
+ }
+ } catch (error: any) {
+ const msg = `Analysis failed: ${error.message}`;
+ ecoOutput.error(`[detection.ts] ${msg}`);
+ smellsViewProvider.setStatus(filePath, 'failed');
+ vscode.window.showErrorMessage(msg);
+ }
+}
+
+/**
+ * Validates conditions before analysis. Only shows notifications when:
+ * - Using cached results (info)
+ * - Server is down (warning)
+ * - No smells configured (warning)
+ *
+ * @returns boolean indicating whether analysis should proceed
+ */
+async function precheckAndMarkQueued(
+ filePath: string,
+ smellsViewProvider: SmellsViewProvider,
+ smellsCacheManager: SmellsCacheManager,
+): Promise {
+ // Validate file scheme and extension
+ const fileUri = vscode.Uri.file(filePath);
+ if (fileUri.scheme !== 'file') {
+ return false;
+ }
+
+ if (!filePath.endsWith('.py')) {
+ return false;
+ }
+
+ // Check for cached results
+ if (smellsCacheManager.hasCachedSmells(filePath)) {
+ const cached = smellsCacheManager.getCachedSmells(filePath);
+ ecoOutput.info(
+ `[detection.ts] Using cached results for ${path.basename(filePath)}`,
+ );
+
+ if (cached && cached.length > 0) {
+ smellsViewProvider.setStatus(filePath, 'passed');
+ smellsViewProvider.setSmells(filePath, cached);
+ } else {
+ smellsViewProvider.setStatus(filePath, 'no_issues');
+ }
+ return false;
+ }
+
+ // Check server availability
+ if (serverStatus.getStatus() === ServerStatusType.DOWN) {
+ const msg = 'Backend server unavailable - using cached results where available';
+ ecoOutput.warn(`[detection.ts] ${msg}`);
+ vscode.window.showWarningMessage(msg);
+ smellsViewProvider.setStatus(filePath, 'server_down');
+ return false;
+ }
+
+ // Verify at least one smell detector is enabled
+ const enabledSmells = getEnabledSmells();
+ if (Object.keys(enabledSmells).length === 0) {
+ const msg = 'No smell detectors enabled in settings';
+ ecoOutput.warn(`[detection.ts] ${msg}`);
+ vscode.window.showWarningMessage(msg);
+ return false;
+ }
+
+ smellsViewProvider.setStatus(filePath, 'queued');
+ return true;
+}
+
+/**
+ * Recursively analyzes Python files in a directory with progress indication.
+ * Shows a progress notification for the folder scan operation.
+ *
+ * @param folderPath - Absolute path to the folder to analyze
+ * @param smellsViewProvider - Provider for updating the UI with results
+ * @param smellsCacheManager - Manager for cached smell results
+ */
+export async function detectSmellsFolder(
+ folderPath: string,
+ smellsViewProvider: SmellsViewProvider,
+ smellsCacheManager: SmellsCacheManager,
+): Promise {
+ return vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Notification,
+ title: `Scanning for Python files in ${path.basename(folderPath)}...`,
+ cancellable: false,
+ },
+ async () => {
+ const pythonFiles: string[] = [];
+
+ // Recursive directory walker for Python files
+ function walk(dir: string): void {
+ try {
+ const entries = fs.readdirSync(dir);
+ for (const entry of entries) {
+ const fullPath = path.join(dir, entry);
+ const stat = fs.statSync(fullPath);
+
+ if (stat.isDirectory()) {
+ walk(fullPath);
+ } else if (stat.isFile() && fullPath.endsWith('.py')) {
+ pythonFiles.push(fullPath);
+ }
+ }
+ } catch (error) {
+ ecoOutput.error(
+ `[detection.ts] Scan error: ${error instanceof Error ? error.message : 'Unknown error'}`,
+ );
+ }
+ }
+
+ walk(folderPath);
+ ecoOutput.info(`[detection.ts] Found ${pythonFiles.length} files to analyze`);
+
+ if (pythonFiles.length === 0) {
+ vscode.window.showWarningMessage(
+ `No Python files found in ${path.basename(folderPath)}`,
+ );
+ return;
+ }
+
+ vscode.window.showInformationMessage(
+ `Analyzing ${pythonFiles.length} Python files...`,
+ );
+
+ // Process each found Python file
+ for (const file of pythonFiles) {
+ await detectSmellsFile(file, smellsViewProvider, smellsCacheManager);
+ }
+ },
+ );
+}
diff --git a/src/commands/detection/wipeWorkCache.ts b/src/commands/detection/wipeWorkCache.ts
new file mode 100644
index 0000000..80533fc
--- /dev/null
+++ b/src/commands/detection/wipeWorkCache.ts
@@ -0,0 +1,30 @@
+import * as vscode from 'vscode';
+
+import { SmellsCacheManager } from '../../context/SmellsCacheManager';
+import { SmellsViewProvider } from '../../providers/SmellsViewProvider';
+
+/**
+ * Clears the smells cache and refreshes the UI.
+ * @param smellsCacheManager - Manages the caching of smells and file hashes.
+ * @param smellsViewProvider - The UI provider for updating the tree view.
+ */
+export async function wipeWorkCache(
+ smellsCacheManager: SmellsCacheManager,
+ smellsViewProvider: SmellsViewProvider,
+) {
+ const userResponse = await vscode.window.showWarningMessage(
+ 'Are you sure you want to clear the entire workspace analysis? This action cannot be undone.',
+ { modal: true },
+ 'Confirm',
+ );
+
+ if (userResponse === 'Confirm') {
+ smellsCacheManager.clearAllCachedSmells();
+ smellsViewProvider.clearAllStatuses();
+ smellsViewProvider.refresh();
+
+ vscode.window.showInformationMessage('Workspace analysis cleared successfully.');
+ } else {
+ vscode.window.showInformationMessage('Operation cancelled.');
+ }
+}
diff --git a/src/commands/refactor/acceptRefactoring.ts b/src/commands/refactor/acceptRefactoring.ts
new file mode 100644
index 0000000..eca8b47
--- /dev/null
+++ b/src/commands/refactor/acceptRefactoring.ts
@@ -0,0 +1,110 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import { SmellsViewProvider } from '../../providers/SmellsViewProvider';
+import { MetricsViewProvider } from '../../providers/MetricsViewProvider';
+import { RefactoringDetailsViewProvider } from '../../providers/RefactoringDetailsViewProvider';
+import { SmellsCacheManager } from '../../context/SmellsCacheManager';
+import { ecoOutput } from '../../extension';
+import { hideRefactorActionButtons } from '../../utils/refactorActionButtons';
+import { detectSmellsFile } from '../detection/detectSmells';
+import { closeAllTrackedDiffEditors } from '../../utils/trackedDiffEditors';
+import { envConfig } from '../../utils/envConfig';
+
+/**
+ * Handles acceptance and application of refactoring changes to the codebase.
+ * Performs the following operations:
+ * 1. Applies refactored changes to target and affected files
+ * 2. Updates energy savings metrics
+ * 3. Clears cached smell data for modified files
+ * 4. Updates UI components to reflect changes
+ *
+ * @param refactoringDetailsViewProvider - Provides access to refactoring details
+ * @param metricsDataProvider - Handles metrics tracking and updates
+ * @param smellsCacheManager - Manages smell detection cache invalidation
+ * @param smellsViewProvider - Controls the smells view UI updates
+ * @param context - VS Code extension context
+ */
+export async function acceptRefactoring(
+ context: vscode.ExtensionContext,
+ refactoringDetailsViewProvider: RefactoringDetailsViewProvider,
+ metricsDataProvider: MetricsViewProvider,
+ smellsCacheManager: SmellsCacheManager,
+ smellsViewProvider: SmellsViewProvider,
+): Promise {
+ const targetFile = refactoringDetailsViewProvider.targetFile;
+ const affectedFiles = refactoringDetailsViewProvider.affectedFiles;
+
+ // Validate refactoring data exists
+ if (!targetFile || !affectedFiles) {
+ console.log('no data');
+ ecoOutput.error('[refactorActions.ts] Error: No refactoring data available');
+ vscode.window.showErrorMessage('No refactoring data available.');
+ return;
+ }
+
+ try {
+ ecoOutput.info(
+ `[refactorActions.ts] Applying refactoring to target file: ${targetFile.original}`,
+ );
+
+ // Apply refactored changes to filesystem
+ fs.copyFileSync(targetFile.refactored, targetFile.original);
+ affectedFiles.forEach((file) => {
+ fs.copyFileSync(file.refactored, file.original);
+ ecoOutput.info(`[refactorActions.ts] Updated affected file: ${file.original}`);
+ });
+
+ // Update metrics if energy savings data exists
+ if (
+ refactoringDetailsViewProvider.energySaved &&
+ refactoringDetailsViewProvider.targetSmell
+ ) {
+ metricsDataProvider.updateMetrics(
+ targetFile.original,
+ refactoringDetailsViewProvider.energySaved,
+ refactoringDetailsViewProvider.targetSmell.symbol,
+ );
+ ecoOutput.info('[refactorActions.ts] Updated energy savings metrics');
+ }
+
+ // Invalidate cache for modified files
+ await Promise.all([
+ smellsCacheManager.clearCachedSmellsForFile(targetFile.original),
+ ...affectedFiles.map((file) =>
+ smellsCacheManager.clearCachedSmellsForFile(file.original),
+ ),
+ ]);
+ ecoOutput.trace('[refactorActions.ts] Cleared smell caches for modified files');
+
+ // Update UI state
+ smellsViewProvider.setStatus(targetFile.original, 'outdated');
+ affectedFiles.forEach((file) => {
+ smellsViewProvider.setStatus(file.original, 'outdated');
+ });
+
+ await detectSmellsFile(
+ targetFile.original,
+ smellsViewProvider,
+ smellsCacheManager,
+ );
+
+ // Reset UI components
+ refactoringDetailsViewProvider.resetRefactoringDetails();
+ closeAllTrackedDiffEditors();
+ hideRefactorActionButtons();
+ smellsViewProvider.refresh();
+
+ context.workspaceState.update(envConfig.UNFINISHED_REFACTORING!, undefined);
+
+ vscode.window.showInformationMessage('Refactoring successfully applied');
+ ecoOutput.info(
+ '[refactorActions.ts] Refactoring changes completed successfully',
+ );
+ } catch (error) {
+ const errorDetails = error instanceof Error ? error.message : 'Unknown error';
+ ecoOutput.error(
+ `[refactorActions.ts] Error applying refactoring: ${errorDetails}`,
+ );
+ vscode.window.showErrorMessage('Failed to apply refactoring. Please try again.');
+ }
+}
diff --git a/src/commands/refactor/refactor.ts b/src/commands/refactor/refactor.ts
new file mode 100644
index 0000000..a57c5a2
--- /dev/null
+++ b/src/commands/refactor/refactor.ts
@@ -0,0 +1,132 @@
+import * as vscode from 'vscode';
+import * as path from 'path';
+
+import { backendRefactorSmell, backendRefactorSmellType } from '../../api/backend';
+import { SmellsViewProvider } from '../../providers/SmellsViewProvider';
+import { RefactoringDetailsViewProvider } from '../../providers/RefactoringDetailsViewProvider';
+import { ecoOutput } from '../../extension';
+import { serverStatus, ServerStatusType } from '../../emitters/serverStatus';
+import {
+ showRefactorActionButtons,
+ hideRefactorActionButtons,
+} from '../../utils/refactorActionButtons';
+import { registerDiffEditor } from '../../utils/trackedDiffEditors';
+import { envConfig } from '../../utils/envConfig';
+
+/**
+ * Orchestrates the complete refactoring workflow.
+ * If isRefactorAllOfType is true, it sends a request to refactor all smells of the same type.
+ *
+ * - Pre-flight validation checks
+ * - Backend communication
+ * - UI updates and diff visualization
+ * - Success/error handling
+ *
+ * Shows carefully selected user notifications for key milestones and errors.
+ */
+export async function refactor(
+ smellsViewProvider: SmellsViewProvider,
+ refactoringDetailsViewProvider: RefactoringDetailsViewProvider,
+ smell: Smell,
+ context: vscode.ExtensionContext,
+ isRefactorAllOfType: boolean = false,
+): Promise {
+ // Log and notify refactoring initiation
+ const action = isRefactorAllOfType
+ ? 'Refactoring all smells of type'
+ : 'Refactoring';
+ ecoOutput.info(`[refactor.ts] ${action} ${smell.symbol} in ${smell.path}`);
+ vscode.window.showInformationMessage(`${action} ${smell.symbol}...`);
+
+ // Validate workspace configuration
+ const workspacePath = context.workspaceState.get(
+ envConfig.WORKSPACE_CONFIGURED_PATH!,
+ );
+
+ if (!workspacePath) {
+ ecoOutput.error('[refactor.ts] Refactoring aborted: No workspace configured');
+ vscode.window.showErrorMessage('Please configure workspace first');
+ return;
+ }
+
+ // Verify backend availability
+ if (serverStatus.getStatus() === ServerStatusType.DOWN) {
+ ecoOutput.warn('[refactor.ts] Refactoring blocked: Backend unavailable');
+ vscode.window.showWarningMessage(
+ 'Cannot refactor - backend service unavailable',
+ );
+ smellsViewProvider.setStatus(smell.path, 'server_down');
+ return;
+ }
+
+ // Update UI state
+ smellsViewProvider.setStatus(smell.path, 'queued');
+ vscode.commands.executeCommand('setContext', 'refactoringInProgress', true);
+
+ try {
+ // Execute backend refactoring
+ ecoOutput.trace(`[refactor.ts] Sending ${action} request...`);
+ const refactoredData = isRefactorAllOfType
+ ? await backendRefactorSmellType(smell, workspacePath)
+ : await backendRefactorSmell(smell, workspacePath);
+
+ ecoOutput.info(
+ `[refactor.ts] Refactoring completed for ${path.basename(smell.path)}. ` +
+ `Energy saved: ${refactoredData.energySaved ?? 'N/A'} kg CO2`,
+ );
+
+ await context.workspaceState.update(envConfig.UNFINISHED_REFACTORING!, {
+ refactoredData,
+ smell,
+ });
+
+ startRefactorSession(smell, refactoredData, refactoringDetailsViewProvider);
+ } catch (error) {
+ ecoOutput.error(
+ `[refactor.ts] Refactoring failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
+ );
+ vscode.window.showErrorMessage('Refactoring failed. See output for details.');
+
+ refactoringDetailsViewProvider.resetRefactoringDetails();
+ hideRefactorActionButtons();
+ smellsViewProvider.setStatus(smell.path, 'failed');
+ } finally {
+ vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
+ }
+}
+
+export async function startRefactorSession(
+ smell: Smell,
+ refactoredData: RefactoredData,
+ refactoringDetailsViewProvider: RefactoringDetailsViewProvider,
+): Promise {
+ // Update refactoring details view
+ refactoringDetailsViewProvider.updateRefactoringDetails(
+ smell,
+ refactoredData.targetFile,
+ refactoredData.affectedFiles,
+ refactoredData.energySaved,
+ );
+
+ // Show diff comparison
+ const targetFile = refactoredData.targetFile;
+ const fileName = path.basename(targetFile.original);
+ await vscode.commands.executeCommand(
+ 'vscode.diff',
+ vscode.Uri.file(targetFile.original),
+ vscode.Uri.file(targetFile.refactored),
+ `Refactoring Comparison (${fileName})`,
+ { preview: false },
+ );
+ registerDiffEditor(
+ vscode.Uri.file(targetFile.original),
+ vscode.Uri.file(targetFile.refactored),
+ );
+
+ await vscode.commands.executeCommand('ecooptimizer.refactorView.focus');
+ showRefactorActionButtons();
+
+ vscode.window.showInformationMessage(
+ `Refactoring complete. Estimated savings: ${refactoredData.energySaved ?? 'N/A'} kg CO2`,
+ );
+}
diff --git a/src/commands/refactor/rejectRefactoring.ts b/src/commands/refactor/rejectRefactoring.ts
new file mode 100644
index 0000000..da1e282
--- /dev/null
+++ b/src/commands/refactor/rejectRefactoring.ts
@@ -0,0 +1,47 @@
+import * as vscode from 'vscode';
+
+import { RefactoringDetailsViewProvider } from '../../providers/RefactoringDetailsViewProvider';
+import { hideRefactorActionButtons } from '../../utils/refactorActionButtons';
+import { closeAllTrackedDiffEditors } from '../../utils/trackedDiffEditors';
+import { SmellsViewProvider } from '../../providers/SmellsViewProvider';
+import { ecoOutput } from '../../extension';
+import { envConfig } from '../../utils/envConfig';
+
+/**
+ * Handles rejection of proposed refactoring changes by:
+ * 1. Resetting UI components
+ * 2. Cleaning up diff editors
+ * 3. Restoring original file states
+ * 4. Providing user feedback
+ *
+ * Only shows a single notification to avoid interrupting workflow.
+ */
+export async function rejectRefactoring(
+ context: vscode.ExtensionContext,
+ refactoringDetailsViewProvider: RefactoringDetailsViewProvider,
+ smellsViewProvider: SmellsViewProvider,
+): Promise {
+ ecoOutput.info('[refactorActions.ts] Refactoring changes discarded');
+ vscode.window.showInformationMessage('Refactoring changes discarded');
+
+ try {
+ // Restore original file status if target exists
+ if (refactoringDetailsViewProvider.targetFile?.original) {
+ const originalPath = refactoringDetailsViewProvider.targetFile.original;
+ smellsViewProvider.setStatus(originalPath, 'passed');
+ ecoOutput.trace(`[refactorActions.ts] Reset status for ${originalPath}`);
+ }
+
+ // Clean up UI components
+ await closeAllTrackedDiffEditors();
+ refactoringDetailsViewProvider.resetRefactoringDetails();
+ hideRefactorActionButtons();
+
+ context.workspaceState.update(envConfig.UNFINISHED_REFACTORING!, undefined);
+
+ ecoOutput.trace('[refactorActions.ts] Refactoring rejection completed');
+ } catch (error) {
+ const errorMsg = `[refactorActions.ts] Error during rejection cleanup: ${error instanceof Error ? error.message : 'Unknown error'}`;
+ ecoOutput.error(errorMsg);
+ }
+}
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
deleted file mode 100644
index 9d9a4d8..0000000
--- a/src/commands/refactorSmell.ts
+++ /dev/null
@@ -1,323 +0,0 @@
-import * as vscode from 'vscode';
-import * as fs from 'fs';
-
-import { envConfig } from '../utils/envConfig';
-
-import { getEditorAndFilePath } from '../utils/editorUtils';
-import { refactorSmell } from '../api/backend';
-import { sidebarState } from '../utils/handleEditorChange';
-
-import { FileHighlighter } from '../ui/fileHighlighter';
-import { ContextManager } from '../context/contextManager';
-import { setTimeout } from 'timers/promises';
-import { serverStatus } from '../utils/serverStatus';
-import { ServerStatusType } from '../utils/serverStatus';
-
-/* istanbul ignore next */
-serverStatus.on('change', (newStatus: ServerStatusType) => {
- console.log('Server status changed:', newStatus);
- if (newStatus === ServerStatusType.DOWN) {
- vscode.window.showWarningMessage('No refactoring is possible at this time.');
- }
-});
-
-export interface MultiRefactoredData {
- tempDirs: string[];
- targetFile: ChangedFile;
- affectedFiles: ChangedFile[];
- energySaved: number;
-}
-
-async function refactorLine(
- smell: Smell,
- filePath: string,
-): Promise {
- try {
- const refactorResult = await refactorSmell(filePath, smell);
- return refactorResult;
- } catch (error) {
- console.error('Error refactoring smell:', error);
- vscode.window.showErrorMessage((error as Error).message);
- return;
- }
-}
-
-export async function refactorSelectedSmell(
- contextManager: ContextManager,
- smellGiven?: Smell,
-): Promise {
- const { editor, filePath } = getEditorAndFilePath();
-
- const pastData = contextManager.getWorkspaceData(
- envConfig.CURRENT_REFACTOR_DATA_KEY!,
- );
-
- // Clean up temp directory if not removed
- if (pastData) {
- console.log('cleaning up temps');
- cleanTemps(pastData);
- }
-
- if (!editor || !filePath) {
- vscode.window.showErrorMessage(
- 'Eco: Unable to proceed as no active editor or file path found.',
- );
- return;
- }
-
- const selectedLine = editor.selection.start.line + 1; // Update to VS Code editor indexing
-
- const smellsData: Smell[] = contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!,
- )[filePath].smells;
-
- if (!smellsData || smellsData.length === 0) {
- vscode.window.showErrorMessage(
- 'Eco: No smells detected in the file for refactoring.',
- );
- return;
- }
-
- // Find the smell to refactor
- let smellToRefactor: Smell | undefined;
- if (smellGiven?.messageId) {
- smellToRefactor = smellsData.find(
- (smell: Smell) =>
- smell.messageId === smellGiven.messageId &&
- smellGiven.occurences[0].line === smell.occurences[0].line,
- );
- } else {
- smellToRefactor = smellsData.find(
- (smell: Smell) => selectedLine === smell.occurences[0].line,
- );
- }
-
- if (!smellToRefactor) {
- vscode.window.showErrorMessage('Eco: No matching smell found for refactoring.');
- return;
- }
-
- await vscode.workspace.save(editor.document.uri);
-
- const refactorResult = await vscode.window.withProgress(
- {
- location: vscode.ProgressLocation.Notification,
- title: `Fetching refactoring for ${smellToRefactor.symbol} on line ${smellToRefactor.occurences[0].line}`,
- },
- async (_progress, _token) => {
- const result = await refactorLine(smellToRefactor, filePath);
-
- if (result && result.refactoredData) {
- vscode.window.showInformationMessage(
- 'Refactoring report available in sidebar.',
- );
- }
-
- return result;
- },
- );
-
- if (!refactorResult || !refactorResult.refactoredData) {
- vscode.window.showErrorMessage(
- 'Eco: Refactoring failed. See console for details.',
- );
- return;
- }
-
- const { refactoredData } = refactorResult;
-
- await startRefactoringSession(contextManager, editor, refactoredData);
-
- if (refactorResult.updatedSmells.length) {
- const fileHighlighter = FileHighlighter.getInstance(contextManager);
- fileHighlighter.highlightSmells(editor, refactorResult.updatedSmells);
- } else {
- vscode.window.showWarningMessage(
- 'Eco: No updated smells detected after refactoring.',
- );
- }
-}
-
-export async function refactorAllSmellsOfType(
- // eslint-disable-next-line unused-imports/no-unused-vars
- contextManager: ContextManager,
- // eslint-disable-next-line unused-imports/no-unused-vars
- smellId: string,
-): Promise {
- // const { editor, filePath } = getEditorAndFilePath();
- // const pastData = contextManager.getWorkspaceData(
- // envConfig.CURRENT_REFACTOR_DATA_KEY!,
- // );
- // // Clean up temp directory if not removed
- // if (pastData) {
- // cleanTemps(pastData);
- // }
- // if (!editor) {
- // vscode.window.showErrorMessage(
- // 'Eco: Unable to proceed as no active editor found.',
- // );
- // console.log('No active editor found to refactor smell. Returning back.');
- // return;
- // }
- // if (!filePath) {
- // vscode.window.showErrorMessage(
- // 'Eco: Unable to proceed as active editor does not have a valid file path.',
- // );
- // console.log('No valid file path found to refactor smell. Returning back.');
- // return;
- // }
- // // only account for one selection to be refactored for now
- // // const selectedLine = editor.selection.start.line + 1; // update to VS code editor indexing
- // const smellsData: Smell[] = contextManager.getWorkspaceData(
- // envConfig.SMELL_MAP_KEY!,
- // )[filePath].smells;
- // if (!smellsData || smellsData.length === 0) {
- // vscode.window.showErrorMessage(
- // 'Eco: No smells detected in the file for refactoring.',
- // );
- // console.log('No smells found in the file for refactoring.');
- // return;
- // }
- // // Filter smells by the given type ID
- // const smellsOfType = smellsData.filter(
- // (smell: Smell) => smell.messageId === smellId,
- // );
- // if (smellsOfType.length === 0) {
- // vscode.window.showWarningMessage(
- // `Eco: No smells of type ${smellId} found in the file.`,
- // );
- // return;
- // }
- // let combinedRefactoredData = '';
- // let totalEnergySaved = 0;
- // let allUpdatedSmells: Smell[] = [];
- // // Refactor each smell of the given type
- // for (const smell of smellsOfType) {
- // const refactorResult = await refactorLine(smell, filePath);
- // if (refactorResult && refactorResult.refactoredData) {
- // // Add two newlines between each refactored result
- // if (combinedRefactoredData) {
- // combinedRefactoredData += '\n\n';
- // }
- // fs.readFile(
- // refactorResult.refactoredData.targetFile.refactored,
- // (err, data) => {
- // if (!err) {
- // combinedRefactoredData += data.toString('utf8');
- // }
- // },
- // );
- // totalEnergySaved += refactorResult.refactoredData.energySaved;
- // if (refactorResult.updatedSmells) {
- // allUpdatedSmells = [...allUpdatedSmells, ...refactorResult.updatedSmells];
- // }
- // }
- // }
- // /*
- // Once all refactorings are merge, need to write to a file so that it has a path that
- // will be the new `targetFile`. Also need to reconstruct the `RefactoredData` object
- // by combining all `affectedFiles` merge to new paths if applicable. Once implemented,
- // just uncomment lines below and pass in the refactoredData.
- // */
- // // Tentative data structure to be built below, change inputs as needed but needs
- // // to implement the `MultiRefactoredData` interface
- // // For any temp files that need to be written due to merging, I'd suggest writing them all
- // // to one temp directory and add that directory to allTempDirs, that way they will be removed
- // // UNCOMMENT ME WHEN READY
- // // const combinedRefactoredData: MultiRefactoredData = {
- // // targetFile: combinedTargetFile,
- // // affectedFiles: allAffectedFiles,
- // // energySaved: totalEnergySaved,
- // // tempDirs: allTempDirs
- // // }
- // // UNCOMMENT ME WHEN READY
- // // startRefactoringSession(contextManager,editor,combinedRefactoredData);
- // if (combinedRefactoredData) {
- // // await RefactorManager.previewRefactor(editor, combinedRefactoredData);
- // vscode.window.showInformationMessage(
- // `Eco: Refactoring completed. Total energy difference: ${totalEnergySaved.toFixed(
- // 4,
- // )}`,
- // );
- // } else {
- // vscode.window.showErrorMessage(
- // 'Eco: Refactoring failed. See console for details.',
- // );
- // return;
- // }
- // if (allUpdatedSmells.length) {
- // const fileHighlighter = FileHighlighter.getInstance(contextManager);
- // fileHighlighter.highlightSmells(editor, allUpdatedSmells);
- // } else {
- // vscode.window.showWarningMessage(
- // 'Eco: No updated smells detected after refactoring.',
- // );
- // }
-}
-
-/* istanbul ignore next */
-async function startRefactoringSession(
- contextManager: ContextManager,
- editor: vscode.TextEditor,
- refactoredData: RefactoredData | MultiRefactoredData,
-): Promise {
- // Store only the diff editor state
- await contextManager.setWorkspaceData(
- envConfig.CURRENT_REFACTOR_DATA_KEY!,
- refactoredData,
- );
-
- await vscode.commands.executeCommand('extension.refactorSidebar.focus');
-
- //Read the refactored code
- const refactoredCode = vscode.Uri.file(refactoredData.targetFile.refactored);
-
- //Get the original code from the editor
- const originalCode = editor.document.uri;
-
- const allFiles: ChangedFile[] = [
- refactoredData.targetFile,
- ...refactoredData.affectedFiles,
- ].map((file) => {
- return {
- original: vscode.Uri.file(file.original).toString(),
- refactored: vscode.Uri.file(file.refactored).toString(),
- };
- });
-
- await contextManager.setWorkspaceData(envConfig.ACTIVE_DIFF_KEY!, {
- files: allFiles,
- firstOpen: true,
- isOpen: true,
- });
-
- await setTimeout(500);
-
- const doc = await vscode.workspace.openTextDocument(originalCode);
- await vscode.window.showTextDocument(doc, { preview: false });
-
- //Show the diff viewer
- sidebarState.isOpening = true;
- vscode.commands.executeCommand(
- 'vscode.diff',
- originalCode,
- refactoredCode,
- 'Refactoring Comparison',
- );
- vscode.commands.executeCommand('ecooptimizer.showRefactorSidebar');
- sidebarState.isOpening = false;
-}
-
-export async function cleanTemps(pastData: any): Promise {
- console.log('Cleaning up stale artifacts');
- const tempDirs =
- (pastData!.tempDir! as string) || (pastData!.tempDirs! as string[]);
-
- if (Array.isArray(tempDirs)) {
- for (const dir in tempDirs) {
- await fs.promises.rm(dir, { recursive: true, force: true });
- }
- } else {
- await fs.promises.rm(tempDirs, { recursive: true, force: true });
- }
-}
diff --git a/src/commands/resetConfiguration.ts b/src/commands/resetConfiguration.ts
new file mode 100644
index 0000000..bd3e004
--- /dev/null
+++ b/src/commands/resetConfiguration.ts
@@ -0,0 +1,35 @@
+import * as vscode from 'vscode';
+import { envConfig } from '../utils/envConfig';
+
+/**
+ * Resets the workspace configuration by clearing the selected workspace path.
+ * Prompts the user for confirmation before performing the reset.
+ *
+ * @param context - The extension context for managing workspace state.
+ */
+export async function resetConfiguration(
+ context: vscode.ExtensionContext,
+): Promise {
+ const confirm = await vscode.window.showWarningMessage(
+ 'Are you sure you want to reset the workspace configuration? This will remove the currently selected workspace and all analysis data will be lost.',
+ { modal: true },
+ 'Reset',
+ );
+
+ if (confirm === 'Reset') {
+ await context.workspaceState.update(
+ envConfig.WORKSPACE_CONFIGURED_PATH!,
+ undefined,
+ );
+
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'workspaceState.workspaceConfigured',
+ false,
+ );
+
+ return true; // signal that reset happened
+ }
+
+ return false;
+}
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index 38229d3..c4f0441 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -3,8 +3,9 @@ import WebSocket from 'ws';
import { initLogs } from '../api/backend';
import { envConfig } from '../utils/envConfig';
-import { serverStatus, ServerStatusType } from '../utils/serverStatus';
-import { globalData } from '../extension';
+import { serverStatus, ServerStatusType } from '../emitters/serverStatus';
+
+const WEBSOCKET_BASE_URL = `ws://${envConfig.SERVER_URL}/logs`;
class LogInitializationError extends Error {
constructor(message: string) {
@@ -13,176 +14,174 @@ class LogInitializationError extends Error {
}
}
-class WebSocketInitializationError extends Error {
- constructor(message: string) {
- super(message);
- this.name = 'WebSocketInitializationError';
- }
-}
-
-const WEBSOCKET_BASE_URL = `ws://${envConfig.SERVER_URL}/logs`;
-
-let websockets: { [key: string]: WebSocket | undefined } = {
- main: undefined,
- detect: undefined,
- refactor: undefined,
-};
-
-let channels: {
- [key: string]: { name: string; channel: vscode.LogOutputChannel | undefined };
-} = {
- main: {
- name: 'EcoOptimizer: Main',
- channel: undefined,
- },
- detect: {
- name: 'EcoOptimizer: Detect',
- channel: undefined,
- },
- refactor: {
- name: 'EcoOptimizer: Refactor',
- channel: undefined,
- },
-};
-
-let CHANNELS_CREATED = false;
-
-serverStatus.on('change', async (newStatus: ServerStatusType) => {
- console.log('Server status changed:', newStatus);
- if (newStatus === ServerStatusType.DOWN) {
- channels.main.channel?.appendLine('Server connection lost');
- } else {
- channels.main.channel?.appendLine('Server connection re-established.');
- await startLogging();
- }
-});
-
-export async function startLogging(retries = 3, delay = 1000): Promise {
- let logInitialized = false;
- const logPath = globalData.contextManager?.context.logUri?.fsPath;
- console.log('log path:', logPath);
-
- if (!logPath) {
- console.error('Missing contextManager or logUri. Cannot initialize logging.');
- return;
+export class LogManager {
+ private websockets: { [key: string]: WebSocket | undefined };
+ private channels: {
+ [key: string]: { name: string; channel: vscode.LogOutputChannel | undefined };
+ };
+ private channelsCreated: boolean;
+ private context: vscode.ExtensionContext;
+
+ constructor(context: vscode.ExtensionContext) {
+ this.context = context;
+ this.websockets = {
+ main: undefined,
+ detect: undefined,
+ refactor: undefined,
+ };
+ this.channels = {
+ main: { name: 'EcoOptimizer: Main', channel: undefined },
+ detect: { name: 'EcoOptimizer: Detect', channel: undefined },
+ refactor: { name: 'EcoOptimizer: Refactor', channel: undefined },
+ };
+ this.channelsCreated = false;
+
+ // Listen for server status changes
+ serverStatus.on('change', async (newStatus: ServerStatusType) => {
+ console.log('Server status changed:', newStatus);
+ if (newStatus === ServerStatusType.DOWN) {
+ this.channels.main.channel?.appendLine('Server connection lost');
+ } else {
+ this.channels.main.channel?.appendLine('Server connection re-established.');
+ await this.startLogging();
+ }
+ });
}
- for (let attempt = 1; attempt <= retries; attempt++) {
- try {
- if (!logInitialized) {
- logInitialized = await initLogs(logPath);
+ /**
+ * Starts the logging process, including initializing logs and WebSockets.
+ * @param retries - Number of retry attempts.
+ * @param delay - Initial delay between retries (in milliseconds).
+ */
+ public async startLogging(retries = 3, delay = 1000): Promise {
+ let logInitialized = false;
+ const logPath = this.context.logUri?.fsPath;
+
+ if (!logPath) {
+ throw new LogInitializationError(
+ 'Missing extension context or logUri. Cannot initialize logging.',
+ );
+ }
+ for (let attempt = 1; attempt <= retries; attempt++) {
+ try {
if (!logInitialized) {
- throw new LogInitializationError(
- `Failed to initialize logs at path: ${logPath}`,
- );
+ logInitialized = await initLogs(logPath);
+
+ if (!logInitialized) {
+ throw new LogInitializationError(
+ `Failed to initialize logs at path: ${logPath}`,
+ );
+ }
+ console.log('Log initialization successful.');
}
- console.log('Log initialization successful.');
- }
- try {
- initializeWebSockets();
+ this.initializeWebSockets();
console.log('Successfully initialized WebSockets. Logging is now active.');
return;
- } catch {
- throw new WebSocketInitializationError('Failed to initialize WebSockets.');
- }
- } catch (error) {
- const err = error as Error;
- console.error(`[Attempt ${attempt}/${retries}] ${err.name}: ${err.message}`);
-
- if (attempt < retries) {
- console.log(`Retrying in ${delay}ms...`);
- await new Promise((resolve) => setTimeout(resolve, delay));
- delay *= 2; // Exponential backoff
- } else {
- console.error('Max retries reached. Logging process failed.');
+ } catch (error) {
+ const err = error as Error;
+ console.error(`[Attempt ${attempt}/${retries}] ${err.name}: ${err.message}`);
+
+ if (attempt < retries) {
+ console.log(`Retrying in ${delay}ms...`);
+ await new Promise((resolve) => setTimeout(resolve, delay));
+ delay *= 2; // Exponential backoff
+ } else {
+ throw new Error('Max retries reached. Logging process failed.');
+ }
}
}
}
-}
-function initializeWebSockets(): void {
- if (!CHANNELS_CREATED) {
- createOutputChannels();
- CHANNELS_CREATED = true;
+ /**
+ * Initializes WebSocket connections for logging.
+ */
+ private initializeWebSockets(): void {
+ if (!this.channelsCreated) {
+ this.createOutputChannels();
+ this.channelsCreated = true;
+ }
+ this.startWebSocket('main');
+ this.startWebSocket('detect');
+ this.startWebSocket('refactor');
}
- startWebSocket('main');
- startWebSocket('detect');
- startWebSocket('refactor');
-}
-function createOutputChannels(): void {
- console.log('Creating ouput channels');
- for (const channel of Object.keys(channels)) {
- channels[channel].channel = vscode.window.createOutputChannel(
- channels[channel].name,
- { log: true },
- );
+ /**
+ * Creates output channels for logging.
+ */
+ private createOutputChannels(): void {
+ console.log('Creating output channels');
+ for (const channel of Object.keys(this.channels)) {
+ this.channels[channel].channel = vscode.window.createOutputChannel(
+ this.channels[channel].name,
+ { log: true },
+ );
+ }
}
-}
-function startWebSocket(logType: string): void {
- const url = `${WEBSOCKET_BASE_URL}/${logType}`;
- const ws = new WebSocket(url);
- websockets[logType] = ws;
-
- ws.on('message', function message(data) {
- const logEvent = data.toString('utf8');
- const level =
- logEvent.match(/\b(ERROR|DEBUG|INFO|WARNING|TRACE)\b/i)?.[0].trim() ||
- 'UNKNOWN';
- const msg = logEvent.split(`[${level}]`, 2)[1].trim();
-
- console.log(logEvent);
- console.log('Level:', level);
-
- switch (level) {
- case 'ERROR': {
- channels[logType].channel!.error(msg);
- break;
- }
- case 'DEBUG': {
- console.log('logging debug');
- channels[logType].channel!.debug(msg);
- break;
- }
- case 'WARNING': {
- channels[logType].channel!.warn(msg);
- break;
- }
- case 'CRITICAL': {
- channels[logType].channel!.error(msg);
- break;
- }
- default: {
- console.log('Logging info');
- channels[logType].channel!.info(msg);
- break;
+ /**
+ * Starts a WebSocket connection for a specific log type.
+ * @param logType - The type of log (e.g., 'main', 'detect', 'refactor').
+ */
+ private startWebSocket(logType: string): void {
+ const url = `${WEBSOCKET_BASE_URL}/${logType}`;
+ const ws = new WebSocket(url);
+ this.websockets[logType] = ws;
+
+ ws.on('message', (data) => {
+ const logEvent = data.toString('utf8');
+ const level =
+ logEvent.match(/\b(ERROR|DEBUG|INFO|WARNING|TRACE)\b/i)?.[0].trim() ||
+ 'UNKNOWN';
+ const msg = logEvent.split(`[${level}]`, 2)[1].trim();
+
+ switch (level) {
+ case 'ERROR': {
+ this.channels[logType].channel!.error(msg);
+ break;
+ }
+ case 'DEBUG': {
+ this.channels[logType].channel!.debug(msg);
+ break;
+ }
+ case 'WARNING': {
+ this.channels[logType].channel!.warn(msg);
+ break;
+ }
+ case 'CRITICAL': {
+ this.channels[logType].channel!.error(msg);
+ break;
+ }
+ default: {
+ this.channels[logType].channel!.info(msg);
+ break;
+ }
}
- }
- });
-
- ws.on('error', function error(err) {
- channels[logType].channel!.error(err);
- });
-
- ws.on('close', function close() {
- channels[logType].channel!.appendLine(
- `WebSocket connection closed for ${channels[logType].name}`,
- );
- });
-
- ws.on('open', function open() {
- channels[logType].channel!.appendLine(`Connected to ${logType} via WebSocket`);
- });
-}
-
-/**
- * Stops watching log files when the extension is deactivated.
- */
-export function stopWatchingLogs(): void {
- Object.values(websockets).forEach((ws) => ws?.close());
+ });
+
+ ws.on('error', (err) => {
+ this.channels[logType].channel!.error(`WebSocket error: ${err.message}`);
+ });
+
+ ws.on('close', () => {
+ this.channels[logType].channel!.appendLine(
+ `WebSocket connection closed for ${this.channels[logType].name}`,
+ );
+ });
+
+ ws.on('open', () => {
+ this.channels[logType].channel!.appendLine(
+ `Connected to ${logType} via WebSocket`,
+ );
+ });
+ }
- Object.values(channels).forEach((channel) => channel.channel?.dispose());
+ /**
+ * Stops watching logs and cleans up resources.
+ */
+ public stopWatchingLogs(): void {
+ Object.values(this.websockets).forEach((ws) => ws?.close());
+ Object.values(this.channels).forEach((channel) => channel.channel?.dispose());
+ }
}
diff --git a/src/commands/toggleSmellLinting.ts b/src/commands/toggleSmellLinting.ts
deleted file mode 100644
index d4b4f54..0000000
--- a/src/commands/toggleSmellLinting.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import * as vscode from 'vscode';
-import { ContextManager } from '../context/contextManager';
-import { detectSmells } from './detectSmells';
-import { FileHighlighter } from '../ui/fileHighlighter'; // Import the class
-import { envConfig } from '../utils/envConfig';
-
-export async function toggleSmellLinting(
- contextManager: ContextManager,
-): Promise {
- const isEnabled = contextManager.getWorkspaceData(
- envConfig.SMELL_LINTING_ENABLED_KEY,
- false,
- );
- const newState = !isEnabled;
-
- // Update state immediately for UI responsiveness
- vscode.commands.executeCommand('setContext', 'eco.smellLintingEnabled', newState);
-
- // Use the singleton instance of FileHighlighter
- const fileHighlighter = FileHighlighter.getInstance(contextManager);
-
- try {
- if (newState) {
- // Run detection and update state on success
- await detectSmells(contextManager); // in the future recieve a true/false
-
- await contextManager.setWorkspaceData(
- envConfig.SMELL_LINTING_ENABLED_KEY,
- newState,
- );
- } else {
- // Clear highlights and update state
- fileHighlighter.resetHighlights(); // Call resetHighlights on the singleton instance
- await contextManager.setWorkspaceData(
- envConfig.SMELL_LINTING_ENABLED_KEY,
- newState,
- );
- vscode.window.showInformationMessage('Eco: Smell linting turned off.');
- }
- } catch (error) {
- console.error('Eco: Error toggling smell linting:', error);
- vscode.window.showErrorMessage('Eco: Failed to toggle smell linting.');
- // Ensure UI state matches actual on error
- vscode.commands.executeCommand(
- 'setContext',
- 'eco.smellLintingEnabled',
- isEnabled,
- );
- }
-}
diff --git a/src/commands/views/exportMetricsData.ts b/src/commands/views/exportMetricsData.ts
new file mode 100644
index 0000000..8fbb512
--- /dev/null
+++ b/src/commands/views/exportMetricsData.ts
@@ -0,0 +1,76 @@
+import * as vscode from 'vscode';
+import { dirname } from 'path';
+import { writeFileSync } from 'fs';
+
+import { MetricsDataItem } from '../../providers/MetricsViewProvider';
+import { envConfig } from '../../utils/envConfig';
+
+/**
+ * Exports collected metrics data to a JSON file in the workspace.
+ * Handles both file and directory workspace paths, saving the output
+ * as 'metrics-data.json' in the appropriate location.
+ *
+ * @param context - Extension context containing metrics data and workspace state
+ */
+export async function exportMetricsData(
+ context: vscode.ExtensionContext,
+): Promise {
+ // Retrieve stored metrics data from extension context
+ const metricsData = context.workspaceState.get<{
+ [path: string]: MetricsDataItem;
+ }>(envConfig.WORKSPACE_METRICS_DATA!, {});
+
+ console.log('metrics data:', metricsData);
+
+ // Early return if no data available
+ if (Object.keys(metricsData).length === 0) {
+ vscode.window.showInformationMessage('No metrics data available to export.');
+ return;
+ }
+
+ // Get configured workspace path from extension context
+ const configuredWorkspacePath = context.workspaceState.get(
+ envConfig.WORKSPACE_CONFIGURED_PATH!,
+ );
+
+ console.log('configured path:', configuredWorkspacePath);
+
+ if (!configuredWorkspacePath) {
+ vscode.window.showErrorMessage('No configured workspace path found.');
+ return;
+ }
+
+ // Determine output file location based on workspace type
+ const workspaceUri = vscode.Uri.file(configuredWorkspacePath);
+ let fileUri: vscode.Uri;
+
+ try {
+ const stat = await vscode.workspace.fs.stat(workspaceUri);
+
+ if (stat.type === vscode.FileType.Directory) {
+ // For directories, save directly in the workspace root
+ fileUri = vscode.Uri.joinPath(workspaceUri, 'metrics-data.json');
+ } else if (stat.type === vscode.FileType.File) {
+ // For single files, save in the parent directory
+ const parentDir = vscode.Uri.file(dirname(configuredWorkspacePath));
+ fileUri = vscode.Uri.joinPath(parentDir, 'metrics-data.json');
+ } else {
+ vscode.window.showErrorMessage('Invalid workspace path type.');
+ return;
+ }
+ } catch (error) {
+ vscode.window.showErrorMessage(`Failed to access workspace path: ${error}`);
+ return;
+ }
+
+ // Write the metrics data to JSON file
+ try {
+ const jsonData = JSON.stringify(metricsData, null, 2);
+ writeFileSync(fileUri.fsPath, jsonData, 'utf-8');
+ vscode.window.showInformationMessage(
+ `Metrics data exported successfully to ${fileUri.fsPath}`,
+ );
+ } catch (error) {
+ vscode.window.showErrorMessage(`Failed to export metrics data: ${error}`);
+ }
+}
diff --git a/src/commands/views/filterSmells.ts b/src/commands/views/filterSmells.ts
new file mode 100644
index 0000000..7a97c8f
--- /dev/null
+++ b/src/commands/views/filterSmells.ts
@@ -0,0 +1,79 @@
+import * as vscode from 'vscode';
+
+import { FilterViewProvider } from '../../providers/FilterViewProvider';
+
+/**
+ * Registers VS Code commands for managing smell filters.
+ * @param context - The VS Code extension context.
+ * @param filterSmellsProvider - The provider responsible for handling smell filtering.
+ */
+export function registerFilterSmellCommands(
+ context: vscode.ExtensionContext,
+ filterSmellsProvider: FilterViewProvider,
+): void {
+ /**
+ * Toggles the state of a specific smell filter.
+ */
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer.toggleSmellFilter',
+ (smellKey: string) => {
+ filterSmellsProvider.toggleSmell(smellKey);
+ },
+ ),
+ );
+
+ /**
+ * Edits a specific smell filter option.
+ * Prompts the user for input, validates the value, and updates the setting.
+ */
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer.editSmellFilterOption',
+ async (item: any) => {
+ if (!item || !item.smellKey || !item.optionKey) {
+ vscode.window.showErrorMessage('Error: Missing smell or option key.');
+ return;
+ }
+
+ const { smellKey, optionKey, value: oldValue } = item;
+
+ const newValue = await vscode.window.showInputBox({
+ prompt: `Enter a new value for ${optionKey}`,
+ value: oldValue?.toString() || '',
+ validateInput: (input) =>
+ isNaN(Number(input)) ? 'Must be a number' : undefined,
+ });
+
+ if (newValue !== undefined && !isNaN(Number(newValue))) {
+ filterSmellsProvider.updateOption(smellKey, optionKey, Number(newValue));
+ filterSmellsProvider.refresh();
+ }
+ },
+ ),
+ );
+
+ /**
+ * Enables all smell filters.
+ */
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.selectAllFilterSmells', () => {
+ filterSmellsProvider.setAllSmellsEnabled(true);
+ }),
+ );
+
+ /**
+ * Disables all smell filters.
+ */
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.deselectAllFilterSmells', () => {
+ filterSmellsProvider.setAllSmellsEnabled(false);
+ }),
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.setFilterDefaults', () => {
+ filterSmellsProvider.resetToDefaults();
+ }),
+ );
+}
diff --git a/src/commands/views/jumpToSmell.ts b/src/commands/views/jumpToSmell.ts
new file mode 100644
index 0000000..7946047
--- /dev/null
+++ b/src/commands/views/jumpToSmell.ts
@@ -0,0 +1,35 @@
+import * as vscode from 'vscode';
+
+/**
+ * Jumps to a specific line in the given file within the VS Code editor.
+ * @param filePath - The absolute path of the file.
+ * @param line - The line number to navigate to.
+ */
+export async function jumpToSmell(filePath: string, line: number): Promise {
+ try {
+ const document = await vscode.workspace.openTextDocument(filePath);
+ const editor = await vscode.window.showTextDocument(document);
+
+ // Move cursor to the specified line
+ const position = new vscode.Position(line, 0);
+ editor.selection = new vscode.Selection(position, position);
+
+ const range = new vscode.Range(position, position);
+ editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
+
+ const flashDecorationType = vscode.window.createTextEditorDecorationType({
+ backgroundColor: new vscode.ThemeColor('editor.wordHighlightBackground'),
+ isWholeLine: true,
+ });
+
+ editor.setDecorations(flashDecorationType, [range]);
+
+ setTimeout(() => {
+ editor.setDecorations(flashDecorationType, []);
+ }, 500);
+ } catch (error: any) {
+ vscode.window.showErrorMessage(
+ `Failed to jump to smell in ${filePath}: ${error.message}`,
+ );
+ }
+}
diff --git a/src/commands/views/openFile.ts b/src/commands/views/openFile.ts
new file mode 100644
index 0000000..47687bf
--- /dev/null
+++ b/src/commands/views/openFile.ts
@@ -0,0 +1,21 @@
+import * as vscode from 'vscode';
+
+/**
+ * Opens a file in the VS Code editor.
+ * Ensures the file is fully opened (not in preview mode).
+ * Displays an error message if no file is selected.
+ *
+ * @param fileUri - The URI of the file to be opened.
+ */
+export async function openFile(fileUri: vscode.Uri) {
+ if (!fileUri) {
+ vscode.window.showErrorMessage('Error: No file selected.');
+ return;
+ }
+
+ await vscode.window.showTextDocument(fileUri, {
+ preview: false, // Ensures the file opens as a permanent tab (not in preview mode)
+ viewColumn: vscode.ViewColumn.Active, // Opens in the active editor column
+ preserveFocus: false, // Focuses the file when opened
+ });
+}
diff --git a/src/commands/wipeWorkCache.ts b/src/commands/wipeWorkCache.ts
deleted file mode 100644
index 345a723..0000000
--- a/src/commands/wipeWorkCache.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import * as vscode from 'vscode';
-import { ContextManager } from '../context/contextManager';
-import { envConfig } from '../utils/envConfig';
-import { updateHash } from '../utils/hashDocs';
-
-export async function wipeWorkCache(
- contextManager: ContextManager,
- reason?: string,
-): Promise {
- try {
- console.log('Eco: Wiping workspace cache...');
-
- // Clear stored smells cache
- await contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, {});
-
- if (reason === 'manual') {
- await contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, {});
- }
-
- // Update file hashes for all open editors
- const visibleEditors = vscode.window.visibleTextEditors;
-
- if (visibleEditors.length === 0) {
- console.log('Eco: No open files to update hash.');
- } else {
- console.log(`Eco: Updating cache for ${visibleEditors.length} visible files.`);
- }
-
- for (const editor of visibleEditors) {
- if (editor.document) {
- await updateHash(contextManager, editor.document);
- }
- }
-
- // β
Determine the appropriate message
- let message = 'Eco: Successfully wiped workspace cache! β
';
- if (reason === 'settings') {
- message =
- 'Eco: Smell detection settings changed. Cache wiped to apply updates. β
';
- } else if (reason === 'manual') {
- message = 'Eco: Workspace cache manually wiped by user. β
';
- }
-
- vscode.window.showInformationMessage(message);
- console.log('Eco:', message);
- } catch (error: any) {
- console.error('Eco: Error while wiping workspace cache:', error);
- vscode.window.showErrorMessage(
- `Eco: Failed to wipe workspace cache. See console for details.`,
- );
- }
-}
diff --git a/src/context/SmellsCacheManager.ts b/src/context/SmellsCacheManager.ts
new file mode 100644
index 0000000..4eb69e4
--- /dev/null
+++ b/src/context/SmellsCacheManager.ts
@@ -0,0 +1,194 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import { createHash } from 'crypto';
+import { envConfig } from '../utils/envConfig';
+import { ecoOutput } from '../extension';
+import { normalizePath } from '../utils/normalizePath';
+
+/**
+ * Manages caching of detected smells to avoid redundant backend calls.
+ * Uses workspace storage to persist cache between sessions.
+ * Implements file content hashing for change detection and maintains
+ * a bidirectional mapping between file paths and their content hashes.
+ */
+export class SmellsCacheManager {
+ // Event emitter for cache update notifications
+ private cacheUpdatedEmitter = new vscode.EventEmitter();
+ public readonly onSmellsUpdated = this.cacheUpdatedEmitter.event;
+
+ constructor(private context: vscode.ExtensionContext) {}
+
+ /**
+ * Generates a stable identifier for a smell based on its properties
+ * @param smell - The smell object to generate ID for
+ * @returns Short SHA-256 hash (first 5 chars) of the serialized smell
+ */
+ private generateSmellId(smell: Smell): string {
+ return createHash('sha256')
+ .update(JSON.stringify(smell))
+ .digest('hex')
+ .substring(0, 5);
+ }
+
+ /**
+ * Generates content hash for a file to detect changes
+ * @param filePath - Absolute path to the file
+ * @returns SHA-256 hash of file content
+ */
+ private generateFileHash(filePath: string): string {
+ const content = fs.readFileSync(filePath, 'utf-8');
+ return createHash('sha256').update(content).digest('hex');
+ }
+
+ /**
+ * Stores smells in cache for specified file
+ * @param filePath - File path to associate with smells
+ * @param smells - Array of smell objects to cache
+ */
+ public async setCachedSmells(filePath: string, smells: Smell[]): Promise {
+ const cache = this.getFullSmellCache();
+ const pathMap = this.getHashToPathMap();
+
+ const normalizedPath = normalizePath(filePath);
+ const fileHash = this.generateFileHash(normalizedPath);
+
+ // Augment smells with stable identifiers
+ const smellsWithIds = smells.map((smell) => ({
+ ...smell,
+ id: this.generateSmellId(smell),
+ }));
+
+ cache[fileHash] = smellsWithIds;
+ pathMap[fileHash] = normalizedPath;
+
+ await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
+ await this.context.workspaceState.update(envConfig.HASH_PATH_MAP_KEY!, pathMap);
+
+ this.cacheUpdatedEmitter.fire(filePath);
+ }
+
+ /**
+ * Retrieves cached smells for a file
+ * @param filePath - File path to look up in cache
+ * @returns Array of smells or undefined if not found
+ */
+ public getCachedSmells(filePath: string): Smell[] | undefined {
+ const normalizedPath = normalizePath(filePath);
+ const fileHash = this.generateFileHash(normalizedPath);
+ const cache = this.getFullSmellCache();
+ return cache[fileHash];
+ }
+
+ /**
+ * Checks if smells exist in cache for a file
+ * @param filePath - File path to check
+ * @returns True if file has cached smells
+ */
+ public hasCachedSmells(filePath: string): boolean {
+ const normalizedPath = normalizePath(filePath);
+ const fileHash = this.generateFileHash(normalizedPath);
+ const cache = this.getFullSmellCache();
+ return cache[fileHash] !== undefined;
+ }
+
+ /**
+ * Clears cache for a file by its current content hash
+ * @param filePath - File path to clear from cache
+ */
+ public async clearCachedSmellsForFile(filePath: string): Promise {
+ const normalizedPath = normalizePath(filePath);
+ const fileHash = this.generateFileHash(normalizedPath);
+ const cache = this.getFullSmellCache();
+ const pathMap = this.getHashToPathMap();
+
+ delete cache[fileHash];
+ delete pathMap[fileHash];
+
+ await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
+ await this.context.workspaceState.update(envConfig.HASH_PATH_MAP_KEY!, pathMap);
+
+ this.cacheUpdatedEmitter.fire(normalizedPath);
+ }
+
+ /**
+ * Clears cache for a file by path (regardless of current content hash)
+ * @param filePath - File path to clear from cache
+ */
+ public async clearCachedSmellsByPath(filePath: string): Promise {
+ const pathMap = this.getHashToPathMap();
+ const normalizedPath = normalizePath(filePath);
+ const hash = Object.keys(pathMap).find((h) => pathMap[h] === normalizedPath);
+ if (!hash) return;
+
+ const cache = this.getFullSmellCache();
+ delete cache[hash];
+ delete pathMap[hash];
+
+ await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
+ await this.context.workspaceState.update(envConfig.HASH_PATH_MAP_KEY!, pathMap);
+
+ this.cacheUpdatedEmitter.fire(normalizedPath);
+ }
+
+ /**
+ * Retrieves complete smell cache
+ * @returns Object mapping file hashes to smell arrays
+ */
+ public getFullSmellCache(): Record {
+ return this.context.workspaceState.get>(
+ envConfig.SMELL_CACHE_KEY!,
+ {},
+ );
+ }
+
+ /**
+ * Retrieves hash-to-path mapping
+ * @returns Object mapping file hashes to original paths
+ */
+ public getHashToPathMap(): Record {
+ return this.context.workspaceState.get>(
+ envConfig.HASH_PATH_MAP_KEY!,
+ {},
+ );
+ }
+
+ /**
+ * Clears entire smell cache
+ */
+ public async clearAllCachedSmells(): Promise {
+ await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, {});
+ await this.context.workspaceState.update(envConfig.HASH_PATH_MAP_KEY!, {});
+
+ this.cacheUpdatedEmitter.fire('all');
+ }
+
+ /**
+ * Retrieves all file paths currently in cache
+ * @returns Array of cached file paths
+ */
+ public getAllFilePaths(): string[] {
+ const map = this.context.workspaceState.get>(
+ envConfig.HASH_PATH_MAP_KEY!,
+ {},
+ );
+ return Object.values(map);
+ }
+
+ /**
+ * Checks if a file has any cache entries (current or historical)
+ * @param filePath - File path to check
+ * @returns True if file exists in cache metadata
+ */
+ public hasFileInCache(filePath: string): boolean {
+ const pathMap = this.getHashToPathMap();
+ const normalizedPath = normalizePath(filePath);
+ const fileExistsInCache = Object.values(pathMap).includes(normalizedPath);
+
+ ecoOutput.debug(
+ `[SmellCacheManager] Path existence check for ${normalizedPath}: ` +
+ `${fileExistsInCache ? 'EXISTS' : 'NOT FOUND'} in cache`,
+ );
+
+ return fileExistsInCache;
+ }
+}
diff --git a/src/context/configManager.ts b/src/context/configManager.ts
new file mode 100644
index 0000000..bd3a216
--- /dev/null
+++ b/src/context/configManager.ts
@@ -0,0 +1,39 @@
+import * as vscode from 'vscode';
+
+export class ConfigManager {
+ private static readonly CONFIG_SECTION = 'ecooptimizer.detection';
+
+ /**
+ * Get a specific configuration value.
+ * @param key The key of the configuration property.
+ * @param _default The default value to return if the configuration property is not found.
+ * @returns The value of the configuration property.
+ */
+ public static get(key: string, _default: any = undefined): T {
+ const config = vscode.workspace.getConfiguration(this.CONFIG_SECTION);
+ return config.get(key, _default);
+ }
+
+ /**
+ * Update a specific configuration value.
+ * @param key The key of the configuration property.
+ * @param value The new value to set.
+ * @param global Whether to update the global configuration or workspace configuration.
+ */
+ public static async update(
+ key: string,
+ value: T,
+ global: boolean = false,
+ ): Promise {
+ const config = vscode.workspace.getConfiguration(this.CONFIG_SECTION);
+ await config.update(key, value, global);
+ }
+
+ /**
+ * Get all configuration values under the ecooptimizer.detection section.
+ * @returns The entire configuration object.
+ */
+ public static getAll(): vscode.WorkspaceConfiguration {
+ return vscode.workspace.getConfiguration(this.CONFIG_SECTION);
+ }
+}
diff --git a/src/context/contextManager.ts b/src/context/contextManager.ts
deleted file mode 100644
index 896255c..0000000
--- a/src/context/contextManager.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import * as vscode from 'vscode';
-
-export class ContextManager {
- public context: vscode.ExtensionContext;
-
- constructor(context: vscode.ExtensionContext) {
- this.context = context;
- }
-
- // Global state example
- public getGlobalData(
- key: string,
- defaultVal: any = undefined,
- ): T | undefined {
- return this.context.globalState.get(key, defaultVal);
- }
-
- public setGlobalData(key: string, value: any): Thenable {
- return this.context.globalState.update(key, value);
- }
-
- // Workspace state example
- public getWorkspaceData(
- key: string,
- defaultVal: any = undefined,
- ): T | undefined {
- return this.context.workspaceState.get(key, defaultVal);
- }
-
- public setWorkspaceData(key: string, value: any): Thenable {
- return this.context.workspaceState.update(key, value);
- }
-}
diff --git a/src/emitters/serverStatus.ts b/src/emitters/serverStatus.ts
new file mode 100644
index 0000000..60bd515
--- /dev/null
+++ b/src/emitters/serverStatus.ts
@@ -0,0 +1,71 @@
+import * as vscode from 'vscode';
+import { EventEmitter } from 'events';
+import { ecoOutput } from '../extension';
+
+/**
+ * Represents possible server connection states
+ */
+export enum ServerStatusType {
+ UNKNOWN = 'unknown', // Initial state before first connection attempt
+ UP = 'up', // Server is available and responsive
+ DOWN = 'down', // Server is unreachable or unresponsive
+}
+
+/**
+ * Tracks and manages backend server connection state with:
+ * - Status change detection
+ * - Appropriate user notifications
+ * - Event emission for dependent components
+ */
+class ServerStatus extends EventEmitter {
+ private status: ServerStatusType = ServerStatusType.UNKNOWN;
+
+ /**
+ * Gets current server connection state
+ * @returns Current ServerStatusType
+ */
+ getStatus(): ServerStatusType {
+ return this.status;
+ }
+
+ /**
+ * Updates server status with change detection and notifications
+ * @param newStatus - Either UP or DOWN status
+ */
+ setStatus(newStatus: ServerStatusType.UP | ServerStatusType.DOWN): void {
+ if (this.status !== newStatus) {
+ const previousStatus = this.status;
+ this.status = newStatus;
+
+ // Log status transition
+ ecoOutput.trace(
+ `[serverStatus.ts] Server status changed from ${previousStatus} to ${newStatus}`,
+ );
+
+ // Handle status-specific notifications
+ if (newStatus === ServerStatusType.UP) {
+ if (previousStatus !== ServerStatusType.UNKNOWN) {
+ ecoOutput.info('[serverStatus.ts] Server connection re-established');
+ vscode.window.showInformationMessage(
+ 'Backend server reconnected - full functionality restored',
+ { modal: false },
+ );
+ }
+ } else {
+ ecoOutput.info('[serverStatus.ts] Server connection lost');
+ vscode.window.showWarningMessage(
+ 'Backend server unavailable - limited functionality',
+ { modal: false },
+ );
+ }
+
+ // Notify subscribers
+ this.emit('change', newStatus);
+ }
+ }
+}
+
+/**
+ * Singleton instance providing global server status management
+ */
+export const serverStatus = new ServerStatus();
diff --git a/src/extension.ts b/src/extension.ts
index 10fc13a..a04e98a 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,306 +1,687 @@
-import { envConfig } from './utils/envConfig';
import * as vscode from 'vscode';
+import path from 'path';
+
+// let port: number;
+
+// export function getApiPort(): number {
+// return port;
+// }
+
+// === Output Channel ===
+export const ecoOutput = vscode.window.createOutputChannel('Eco-Optimizer', {
+ log: true,
+});
+
+// === Smell Linting ===
+let smellLintingEnabled = false;
-import { detectSmells } from './commands/detectSmells';
+export function isSmellLintingEnabled(): boolean {
+ return smellLintingEnabled;
+}
+
+// === In-Built ===
+import { existsSync, promises } from 'fs';
+
+// === Core Utilities ===
+import { envConfig } from './utils/envConfig';
+import { getNameByMessageId, loadSmells } from './utils/smellsData';
+import { initializeStatusesFromCache } from './utils/initializeStatusesFromCache';
+import { checkServerStatus } from './api/backend';
+
+// === Context & View Providers ===
+import { SmellsCacheManager } from './context/SmellsCacheManager';
import {
- refactorSelectedSmell,
- refactorAllSmellsOfType,
-} from './commands/refactorSmell';
-import { wipeWorkCache } from './commands/wipeWorkCache';
-import { stopWatchingLogs } from './commands/showLogs';
-import { ContextManager } from './context/contextManager';
+ SmellsViewProvider,
+ SmellTreeItem,
+ TreeItem,
+} from './providers/SmellsViewProvider';
+import { MetricsViewProvider } from './providers/MetricsViewProvider';
+import { FilterViewProvider } from './providers/FilterViewProvider';
+import { RefactoringDetailsViewProvider } from './providers/RefactoringDetailsViewProvider';
+
+// === Commands ===
+import { configureWorkspace } from './commands/configureWorkspace';
+import { resetConfiguration } from './commands/resetConfiguration';
import {
- getEnabledSmells,
- handleSmellFilterUpdate,
-} from './utils/handleSmellSettings';
-import { updateHash } from './utils/hashDocs';
-import { RefactorSidebarProvider } from './ui/refactorView';
-import { handleEditorChanges } from './utils/handleEditorChange';
+ detectSmellsFile,
+ detectSmellsFolder,
+} from './commands/detection/detectSmells';
+import { registerFilterSmellCommands } from './commands/views/filterSmells';
+import { jumpToSmell } from './commands/views/jumpToSmell';
+import { wipeWorkCache } from './commands/detection/wipeWorkCache';
+import { refactor, startRefactorSession } from './commands/refactor/refactor';
+import { acceptRefactoring } from './commands/refactor/acceptRefactoring';
+import { rejectRefactoring } from './commands/refactor/rejectRefactoring';
+import { exportMetricsData } from './commands/views/exportMetricsData';
+
+// === Listeners & UI ===
+import { WorkspaceModifiedListener } from './listeners/workspaceModifiedListener';
+import { FileHighlighter } from './ui/fileHighlighter';
import { LineSelectionManager } from './ui/lineSelectionManager';
-import { checkServerStatus } from './api/backend';
-import { serverStatus } from './utils/serverStatus';
-
-import { toggleSmellLinting } from './commands/toggleSmellLinting';
+import { HoverManager } from './ui/hoverManager';
+import {
+ closeAllTrackedDiffEditors,
+ registerDiffEditor,
+} from './utils/trackedDiffEditors';
+import { initializeRefactorActionButtons } from './utils/refactorActionButtons';
+import { LogManager } from './commands/showLogs';
-export const globalData: { contextManager?: ContextManager } = {
- contextManager: undefined,
-};
+// === Backend Server ===
+// import { ServerProcess } from './lib/processManager';
+// import { DependencyManager } from './lib/dependencyManager';
-export function activate(context: vscode.ExtensionContext): void {
- console.log('Eco: Refactor Plugin Activated Successfully');
- const contextManager = new ContextManager(context);
+let backendLogManager: LogManager;
+// let server: ServerProcess;
- globalData.contextManager = contextManager;
+export async function activate(context: vscode.ExtensionContext): Promise {
+ ecoOutput.info('Initializing Eco-Optimizer extension...');
+ console.log('Initializing Eco-Optimizer extension...');
- // Show the settings popup if needed
- // TODO: Setting to re-enable popup if disabled
- const settingsPopupChoice =
- contextManager.getGlobalData('showSettingsPopup');
+ // === Install and Run Backend Server ====
+ // if (!(await DependencyManager.ensureDependencies(context))) {
+ // vscode.window.showErrorMessage(
+ // 'Cannot run the extension without the ecooptimizer server. Deactivating extension.',
+ // );
+ // }
- if (settingsPopupChoice === undefined || settingsPopupChoice) {
- showSettingsPopup();
- }
+ // server = new ServerProcess(context);
+ // try {
+ // port = await server.start();
- console.log('environment variables:', envConfig);
+ // console.log(`Server started on port ${port}`);
+ // } catch (error) {
+ // vscode.window.showErrorMessage(`Failed to start server: ${error}`);
+ // }
- checkServerStatus();
+ backendLogManager = new LogManager(context);
- let smellsData = contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!) || {};
- contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, smellsData);
+ // === Load Core Data ===
+ loadSmells();
- let fileHashes =
- contextManager.getWorkspaceData(envConfig.FILE_CHANGES_KEY!) || {};
- contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, fileHashes);
-
- // Check server health every 10 seconds
+ // === Start periodic backend status checks ===
+ checkServerStatus();
setInterval(checkServerStatus, 10000);
- // ===============================================================
- // REGISTER COMMANDS
- // ===============================================================
+ // === Initialize Refactor Action Buttons ===
+ initializeRefactorActionButtons(context);
+
+ // === Initialize Managers & Providers ===
+ const smellsCacheManager = new SmellsCacheManager(context);
+ const smellsViewProvider = new SmellsViewProvider(context);
+ const metricsViewProvider = new MetricsViewProvider(context);
+ const filterSmellsProvider = new FilterViewProvider(
+ context,
+ metricsViewProvider,
+ smellsCacheManager,
+ smellsViewProvider,
+ );
+ const refactoringDetailsViewProvider = new RefactoringDetailsViewProvider();
+
+ initializeStatusesFromCache(context, smellsCacheManager, smellsViewProvider);
- // Detect Smells Command
+ // === Register Tree Views ===
context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.detectSmells', async () => {
- console.log('Eco: Detect Smells Command Triggered');
- detectSmells(contextManager);
+ vscode.window.createTreeView('ecooptimizer.smellsView', {
+ treeDataProvider: smellsViewProvider,
+ }),
+ vscode.window.createTreeView('ecooptimizer.metricsView', {
+ treeDataProvider: metricsViewProvider,
+ showCollapseAll: true,
+ }),
+ vscode.window.createTreeView('ecooptimizer.filterView', {
+ treeDataProvider: filterSmellsProvider,
+ showCollapseAll: true,
+ }),
+ vscode.window.createTreeView('ecooptimizer.refactorView', {
+ treeDataProvider: refactoringDetailsViewProvider,
}),
);
- // Refactor Selected Smell Command
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.refactorSmell', () => {
- if (serverStatus.getStatus() === 'up') {
- console.log('Eco: Refactor Selected Smell Command Triggered');
- refactorSelectedSmell(contextManager);
- } else {
- vscode.window.showWarningMessage('Action blocked: Server is down.');
- }
+ filterSmellsProvider.setTreeView(
+ vscode.window.createTreeView('ecooptimizer.filterView', {
+ treeDataProvider: filterSmellsProvider,
+ showCollapseAll: true,
}),
);
- // Refactor All Smells of Type Command
+ const workspaceConfigured = context.workspaceState.get(
+ envConfig.WORKSPACE_CONFIGURED_PATH!,
+ );
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'workspaceState.workspaceConfigured',
+ Boolean(workspaceConfigured),
+ );
+
+ // === Register Commands ===
context.subscriptions.push(
+ // vscode.commands.registerCommand('ecooptimizer.startServer', async () => {
+ // port = await server.start();
+ // }),
+ // vscode.commands.registerCommand('ecooptimizer.stopServer', async () => {
+ // server.dispose();
+ // }),
+ vscode.commands.registerCommand('ecooptimizer.configureWorkspace', async () => {
+ await configureWorkspace(context);
+ smellsViewProvider.refresh();
+ metricsViewProvider.refresh();
+ }),
+
+ vscode.commands.registerCommand('ecooptimizer.resetConfiguration', async () => {
+ const didReset = await resetConfiguration(context);
+ if (didReset) {
+ smellsCacheManager.clearAllCachedSmells();
+ smellsViewProvider.clearAllStatuses();
+ smellsViewProvider.refresh();
+ metricsViewProvider.refresh();
+ vscode.window.showInformationMessage(
+ 'Workspace configuration and analysis data have been reset.',
+ );
+ }
+ }),
+
+ vscode.commands.registerCommand('ecooptimizer.jumpToSmell', jumpToSmell),
+
+ vscode.commands.registerCommand('ecooptimizer.wipeWorkCache', async () => {
+ await wipeWorkCache(smellsCacheManager, smellsViewProvider);
+ }),
+
vscode.commands.registerCommand(
- 'ecooptimizer.refactorAllSmellsOfType',
- async (smellId: string) => {
- if (serverStatus.getStatus() === 'up') {
- console.log(
- `Eco: Refactor All Smells of Type Command Triggered for ${smellId}`,
+ 'ecooptimizer.detectSmellsFile',
+ async (fileItem: TreeItem) => {
+ let filePath: string;
+ if (!fileItem) {
+ const allPythonFiles: vscode.QuickPickItem[] = [];
+ const folderPath = workspaceConfigured;
+
+ if (!folderPath) {
+ vscode.window.showWarningMessage('No workspace configured.');
+ return;
+ }
+
+ const gatherPythonFiles = async (dirPath: string): Promise => {
+ const files = await vscode.workspace.fs.readDirectory(
+ vscode.Uri.file(dirPath),
+ );
+ for (const [name, type] of files) {
+ const fullPath = path.join(dirPath, name);
+ if (type === vscode.FileType.File && name.endsWith('.py')) {
+ const relativePath = path.relative(folderPath, fullPath);
+ allPythonFiles.push({
+ label: `${name}`,
+ description: `${path.dirname(relativePath) === '.' ? undefined : path.dirname(relativePath)}`,
+ iconPath: new vscode.ThemeIcon('symbol-file'),
+ });
+ } else if (type === vscode.FileType.Directory) {
+ await gatherPythonFiles(fullPath); // Recursively gather Python files in subdirectories
+ }
+ }
+ };
+
+ const currentFile = vscode.window.activeTextEditor?.document.fileName;
+ if (currentFile && currentFile.endsWith('.py')) {
+ const relativePath = path.relative(folderPath, currentFile);
+ allPythonFiles.push({
+ label: `${path.basename(currentFile)}`,
+ description: `${path.dirname(relativePath) === '.' ? undefined : path.dirname(relativePath)}`,
+ detail: 'Current File',
+ iconPath: new vscode.ThemeIcon('symbol-file'),
+ });
+
+ allPythonFiles.push({
+ label: 'βββββββββββββββ',
+ kind: vscode.QuickPickItemKind.Separator,
+ });
+ }
+
+ await gatherPythonFiles(folderPath);
+
+ if (allPythonFiles.length === 0) {
+ vscode.window.showWarningMessage(
+ 'No Python files found in the workspace.',
+ );
+ return;
+ }
+
+ const selectedFile = await vscode.window.showQuickPick(allPythonFiles, {
+ title: 'Select a Python file to analyze',
+ placeHolder: 'Choose a Python file from the workspace',
+ canPickMany: false,
+ });
+
+ if (!selectedFile) {
+ vscode.window.showWarningMessage('No file selected.');
+ return;
+ }
+
+ filePath = path.join(
+ folderPath,
+ selectedFile.description!,
+ selectedFile.label,
);
- refactorAllSmellsOfType(contextManager, smellId);
} else {
- vscode.window.showWarningMessage('Action blocked: Server is down.');
+ if (!(fileItem instanceof vscode.TreeItem)) {
+ vscode.window.showWarningMessage('Invalid file item selected.');
+ return;
+ }
+ filePath = fileItem.resourceUri!.fsPath;
+ if (!filePath) {
+ vscode.window.showWarningMessage('Please select a file to analyze.');
+ return;
+ }
}
+ detectSmellsFile(filePath, smellsViewProvider, smellsCacheManager);
},
),
- );
- // Wipe Cache Command
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.wipeWorkCache', async () => {
- console.log('Eco: Wipe Work Cache Command Triggered');
- vscode.window.showInformationMessage(
- 'Eco: Manually wiping workspace memory... β
',
- );
- await wipeWorkCache(contextManager, 'manual');
- }),
- );
+ vscode.commands.registerCommand(
+ 'ecooptimizer.detectSmellsFolder',
+ async (folderItem: vscode.TreeItem) => {
+ let folderPath: string;
+ if (!folderItem) {
+ if (!workspaceConfigured) {
+ vscode.window.showWarningMessage('No workspace configured.');
+ return;
+ }
- // screen button go brr
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.toggleSmellLinting', () => {
- console.log('Eco: Toggle Smell Linting Command Triggered');
- toggleSmellLinting(contextManager);
- }),
- );
+ const allDirectories: vscode.QuickPickItem[] = [];
+ const directoriesWithPythonFiles = new Set();
+
+ const gatherDirectories = async (
+ dirPath: string,
+ relativePath = '',
+ ): Promise => {
+ const files = await vscode.workspace.fs.readDirectory(
+ vscode.Uri.file(dirPath),
+ );
+ let hasPythonFile = false;
+
+ for (const [name, type] of files) {
+ const fullPath = path.join(dirPath, name);
+ const newRelativePath = path.join(relativePath, name);
+ if (type === vscode.FileType.File && name.endsWith('.py')) {
+ hasPythonFile = true;
+ } else if (type === vscode.FileType.Directory) {
+ const subDirHasPythonFile = await gatherDirectories(
+ fullPath,
+ newRelativePath,
+ );
+ if (subDirHasPythonFile) {
+ hasPythonFile = true;
+ }
+ }
+ }
+
+ if (hasPythonFile) {
+ directoriesWithPythonFiles.add(dirPath);
+ const isDirectChild = relativePath.split(path.sep).length === 1;
+ allDirectories.push({
+ label: `${path.basename(dirPath)}`,
+ description: isDirectChild ? undefined : path.dirname(relativePath),
+ iconPath: new vscode.ThemeIcon('folder'),
+ });
+ }
+
+ return hasPythonFile;
+ };
+
+ await gatherDirectories(workspaceConfigured);
+
+ if (allDirectories.length === 0) {
+ vscode.window.showWarningMessage(
+ 'No directories with Python files found in the workspace.',
+ );
+ return;
+ }
- // ===============================================================
- // REGISTER VIEWS
- // ===============================================================
+ const selectedDirectory = await vscode.window.showQuickPick(
+ allDirectories,
+ {
+ title: 'Select a directory to analyze',
+ placeHolder: 'Choose a directory with Python files from the workspace',
+ canPickMany: false,
+ },
+ );
- const refactorProvider = new RefactorSidebarProvider(context);
- context.subscriptions.push(
- vscode.window.registerWebviewViewProvider(
- RefactorSidebarProvider.viewType,
- refactorProvider,
+ if (!selectedDirectory) {
+ vscode.window.showWarningMessage('No directory selected.');
+ return;
+ }
+
+ folderPath = path.join(
+ workspaceConfigured,
+ selectedDirectory.description
+ ? path.join(
+ selectedDirectory.description,
+ path.basename(selectedDirectory.label),
+ )
+ : path.basename(selectedDirectory.label),
+ );
+ } else {
+ if (!(folderItem instanceof vscode.TreeItem)) {
+ vscode.window.showWarningMessage('Invalid folder item selected.');
+ return;
+ }
+ folderPath = folderItem.resourceUri!.fsPath;
+ }
+ detectSmellsFolder(folderPath, smellsViewProvider, smellsCacheManager);
+ },
),
- );
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.showRefactorSidebar', () =>
- refactorProvider.updateView(),
+ vscode.commands.registerCommand(
+ 'ecooptimizer.refactorSmell',
+ (item: SmellTreeItem | Smell) => {
+ let smell: Smell;
+ if (item instanceof SmellTreeItem) {
+ smell = item.smell;
+ } else {
+ smell = item;
+ }
+ if (!smell) {
+ vscode.window.showErrorMessage('No code smell detected for this item.');
+ return;
+ }
+ refactor(smellsViewProvider, refactoringDetailsViewProvider, smell, context);
+ },
),
- );
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.pauseRefactorSidebar', () =>
- refactorProvider.pauseView(),
+ vscode.commands.registerCommand(
+ 'ecooptimizer.refactorAllSmellsOfType',
+ async (item: TreeItem | { fullPath: string; smellType: string }) => {
+ let filePath = item.fullPath;
+ if (!filePath) {
+ vscode.window.showWarningMessage(
+ 'Unable to get file path for smell refactoring.',
+ );
+ return;
+ }
+
+ const cachedSmells = smellsCacheManager.getCachedSmells(filePath);
+ if (!cachedSmells || cachedSmells.length === 0) {
+ vscode.window.showInformationMessage('No smells detected in this file.');
+ return;
+ }
+
+ ecoOutput.info(`π‘ Found ${cachedSmells.length} smells in ${filePath}`);
+
+ const uniqueMessageIds = new Set();
+ for (const smell of cachedSmells) {
+ uniqueMessageIds.add(smell.messageId);
+ }
+
+ let selectedSmell: string;
+ if (item instanceof TreeItem) {
+ const quickPickItems: vscode.QuickPickItem[] = Array.from(
+ uniqueMessageIds,
+ ).map((id) => {
+ const name = getNameByMessageId(id) ?? id;
+ return {
+ label: name,
+ description: id,
+ };
+ });
+
+ const selected = await vscode.window.showQuickPick(quickPickItems, {
+ title: 'Select a smell type to refactor',
+ placeHolder: 'Choose the type of smell you want to refactor',
+ matchOnDescription: false,
+ matchOnDetail: false,
+ ignoreFocusOut: false,
+ canPickMany: false,
+ });
+
+ if (!selected) {
+ return;
+ }
+ selectedSmell = selected.description!;
+ } else {
+ selectedSmell = item.smellType;
+ }
+
+ const firstSmell = cachedSmells.find(
+ (smell) => smell.messageId === selectedSmell,
+ );
+
+ if (!firstSmell) {
+ vscode.window.showWarningMessage('No smells found for the selected type.');
+ return;
+ }
+
+ ecoOutput.info(
+ `π Triggering refactorAllSmellsOfType for: ${selectedSmell}`,
+ );
+
+ await refactor(
+ smellsViewProvider,
+ refactoringDetailsViewProvider,
+ firstSmell,
+ context,
+ true, // isRefactorAllOfType
+ );
+ },
),
- );
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.clearRefactorSidebar', () =>
- refactorProvider.clearView(),
+ vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', async () => {
+ await acceptRefactoring(
+ context,
+ refactoringDetailsViewProvider,
+ metricsViewProvider,
+ smellsCacheManager,
+ smellsViewProvider,
+ );
+ }),
+
+ vscode.commands.registerCommand('ecooptimizer.rejectRefactoring', async () => {
+ await rejectRefactoring(
+ context,
+ refactoringDetailsViewProvider,
+ smellsViewProvider,
+ );
+ }),
+
+ vscode.commands.registerCommand(
+ 'ecooptimizer.openDiffEditor',
+ (originalFilePath: string, refactoredFilePath: string) => {
+ // Get the file name for the diff editor title
+ const fileName = path.basename(originalFilePath);
+
+ // Show the diff editor with the updated title
+ const originalUri = vscode.Uri.file(originalFilePath);
+ const refactoredUri = vscode.Uri.file(refactoredFilePath);
+ vscode.commands.executeCommand(
+ 'vscode.diff',
+ originalUri,
+ refactoredUri,
+ `Refactoring Comparison (${fileName})`,
+ {
+ preview: false,
+ },
+ );
+
+ registerDiffEditor(originalUri, refactoredUri);
+ },
),
- );
- // ===============================================================
- // ADD LISTENERS
- // ===============================================================
+ vscode.commands.registerCommand('ecooptimizer.exportMetricsData', () => {
+ exportMetricsData(context);
+ }),
- // Register a listener for configuration changes
- context.subscriptions.push(
- vscode.workspace.onDidChangeConfiguration((event) => {
- handleConfigurationChange(event);
+ vscode.commands.registerCommand('ecooptimizer.metricsView.refresh', () => {
+ metricsViewProvider.refresh();
+ }),
+
+ vscode.commands.registerCommand('ecooptimizer.clearMetricsData', () => {
+ vscode.window
+ .showWarningMessage(
+ 'Clear all metrics data? This cannot be undone unless you have exported it.',
+ { modal: true },
+ 'Clear',
+ 'Cancel',
+ )
+ .then((selection) => {
+ if (selection === 'Clear') {
+ context.workspaceState.update(
+ envConfig.WORKSPACE_METRICS_DATA!,
+ undefined,
+ );
+ metricsViewProvider.refresh();
+ vscode.window.showInformationMessage('Metrics data cleared.');
+ }
+ });
}),
);
- vscode.window.onDidChangeVisibleTextEditors(async (editors) => {
- handleEditorChanges(contextManager, editors);
- });
+ // === Register Filter UI Commands ===
+ registerFilterSmellCommands(context, filterSmellsProvider);
- // Adds comments to lines describing the smell
- const lineSelectManager = new LineSelectionManager(contextManager);
+ // === Workspace File Listener ===
context.subscriptions.push(
- vscode.window.onDidChangeTextEditorSelection((event) => {
- console.log('Eco: Detected line selection event');
- lineSelectManager.commentLine(event.textEditor);
- }),
+ new WorkspaceModifiedListener(
+ context,
+ smellsCacheManager,
+ smellsViewProvider,
+ metricsViewProvider,
+ ),
);
- // Updates directory of file states (for checking if modified)
+ // === File Highlighting ===
+ const fileHighlighter = FileHighlighter.getInstance(smellsCacheManager);
+
+ fileHighlighter.updateHighlightsForVisibleEditors();
+
+ // === Line Selection ===
+ const lineSelectManager = new LineSelectionManager(smellsCacheManager);
context.subscriptions.push(
- vscode.workspace.onDidSaveTextDocument(async (document) => {
- console.log('Eco: Detected document saved event');
- await updateHash(contextManager, document);
+ vscode.window.onDidChangeTextEditorSelection((event) => {
+ const textEditor = event.textEditor;
+ if (!textEditor.document.fileName.endsWith('.py')) {
+ return;
+ }
+ lineSelectManager.commentLine(textEditor);
}),
);
- // Handles case of documents already being open on VS Code open
- vscode.window.visibleTextEditors.forEach(async (editor) => {
- if (editor.document) {
- await updateHash(contextManager, editor.document);
+ // == Hover Manager ===
+ const hoverManager = new HoverManager(smellsCacheManager);
+ hoverManager.register(context);
+
+ // === Smell Linting ===
+ const updateSmellLintingContext = (): void => {
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'ecooptimizer.smellLintingEnabled',
+ smellLintingEnabled,
+ );
+ };
+
+ const lintActiveEditors = (): void => {
+ for (const editor of vscode.window.visibleTextEditors) {
+ const filePath = editor.document.uri.fsPath;
+ detectSmellsFile(filePath, smellsViewProvider, smellsCacheManager);
+ ecoOutput.info(
+ `[WorkspaceListener] Smell linting is ON β auto-detecting smells for ${filePath}`,
+ );
}
- });
+ };
+
+ const toggleSmellLinting = (): void => {
+ smellLintingEnabled = !smellLintingEnabled;
+ updateSmellLintingContext();
+ const msg = smellLintingEnabled
+ ? 'Smell linting enabled'
+ : 'Smell linting disabled';
+ lintActiveEditors();
+ vscode.window.showInformationMessage(msg);
+ };
- // Initializes first state of document when opened while extension is active
context.subscriptions.push(
- vscode.workspace.onDidOpenTextDocument(async (document) => {
- console.log('Eco: Detected document opened event');
- await updateHash(contextManager, document);
- }),
+ vscode.commands.registerCommand(
+ 'ecooptimizer.toggleSmellLintingOn',
+ toggleSmellLinting,
+ ),
+ vscode.commands.registerCommand(
+ 'ecooptimizer.toggleSmellLintingOff',
+ toggleSmellLinting,
+ ),
);
- // Listen for file save events
+ // === File View Change Listner ===
context.subscriptions.push(
- vscode.workspace.onDidSaveTextDocument(async (document) => {
- console.log('Eco: Detected document saved event');
+ vscode.window.onDidChangeVisibleTextEditors(() => {
+ fileHighlighter.updateHighlightsForVisibleEditors();
- // Check if smell linting is enabled
- const isEnabled = contextManager.getWorkspaceData(
- envConfig.SMELL_LINTING_ENABLED_KEY,
- false,
- );
- if (isEnabled) {
- console.log('Eco: Smell linting is enabled. Detecting smells...');
- await detectSmells(contextManager);
+ if (smellLintingEnabled) {
+ lintActiveEditors();
}
}),
);
- // Listen for editor changes
- context.subscriptions.push(
- vscode.window.onDidChangeActiveTextEditor(async (editor) => {
- if (editor) {
- console.log('Eco: Detected editor change event');
-
- // Check if the file is a Python file
- if (editor.document.languageId === 'python') {
- console.log('Eco: Active file is a Python file.');
-
- // Check if smell linting is enabled
- const isEnabled = contextManager.getWorkspaceData(
- envConfig.SMELL_LINTING_ENABLED_KEY,
- false,
+ const cleanPastSessionArtifacts = async (): Promise => {
+ const pastData = context.workspaceState.get(
+ envConfig.UNFINISHED_REFACTORING!,
+ );
+
+ if (pastData) {
+ const tempDir = pastData.refactoredData.tempDir;
+
+ try {
+ const tempDirExists = existsSync(tempDir);
+
+ if (tempDirExists) {
+ const userChoice = await vscode.window.showWarningMessage(
+ 'A previous refactoring session was detected. Would you like to continue or discard it?',
+ { modal: true },
+ 'Continue',
+ 'Discard',
);
- if (isEnabled) {
- console.log('Eco: Smell linting is enabled. Detecting smells...');
- await detectSmells(contextManager);
+
+ if (userChoice === 'Discard') {
+ await promises.rm(tempDir, { recursive: true, force: true });
+
+ context.workspaceState.update(
+ envConfig.UNFINISHED_REFACTORING!,
+ undefined,
+ );
+
+ closeAllTrackedDiffEditors();
+ } else if (userChoice === 'Continue') {
+ ecoOutput.info('Resuming previous refactoring session...');
+ startRefactorSession(
+ pastData.smell,
+ pastData.refactoredData,
+ refactoringDetailsViewProvider,
+ );
+ return;
}
}
+ } catch (error) {
+ ecoOutput.error(`Error handling past refactoring session: ${error}`);
+ context.workspaceState.update(envConfig.UNFINISHED_REFACTORING!, undefined);
}
- }),
- );
+ }
+ };
- // ===============================================================
- // HANDLE SMELL FILTER CHANGES
- // ===============================================================
+ cleanPastSessionArtifacts();
- let previousSmells = getEnabledSmells();
- vscode.workspace.onDidChangeConfiguration((event) => {
- if (event.affectsConfiguration('ecooptimizer.enableSmells')) {
- console.log('Eco: Smell preferences changed! Wiping cache.');
- handleSmellFilterUpdate(previousSmells, contextManager);
- previousSmells = getEnabledSmells();
- }
- });
-}
+ // if (!port) {
+ // try {
+ // port = await server.start();
-function showSettingsPopup(): void {
- // Check if the required settings are already configured
- const config = vscode.workspace.getConfiguration('ecooptimizer');
- const workspacePath = config.get('projectWorkspacePath', '');
- const logsOutputPath = config.get('logsOutputPath', '');
- const unitTestPath = config.get('unitTestPath', '');
-
- // If settings are not configured, prompt the user to configure them
- if (!workspacePath || !logsOutputPath || !unitTestPath) {
- vscode.window
- .showInformationMessage(
- 'Please configure the paths for your workspace and logs.',
- { modal: true },
- 'Continue', // Button to open settings
- 'Skip', // Button to dismiss
- 'Never show this again',
- )
- .then((selection) => {
- if (selection === 'Continue') {
- // Open the settings page filtered to extension's settings
- vscode.commands.executeCommand(
- 'workbench.action.openSettings',
- 'ecooptimizer',
- );
- } else if (selection === 'Skip') {
- // Inform user they can configure later
- vscode.window.showInformationMessage(
- 'You can configure the paths later in the settings.',
- );
- } else if (selection === 'Never show this again') {
- globalData.contextManager!.setGlobalData('showSettingsPopup', false);
- vscode.window.showInformationMessage(
- 'You can re-enable this popup again in the settings.',
- );
- }
- });
- }
-}
+ // console.log(`Server started on port ${port}`);
+ // } catch (error) {
+ // vscode.window.showErrorMessage(`Failed to start server: ${error}`);
+ // }
+ // }
-function handleConfigurationChange(event: vscode.ConfigurationChangeEvent): void {
- // Check if any relevant setting was changed
- if (
- event.affectsConfiguration('ecooptimizer.projectWorkspacePath') ||
- event.affectsConfiguration('ecooptimizer.unitTestCommand') ||
- event.affectsConfiguration('ecooptimizer.logsOutputPath')
- ) {
- // Display a warning message about changing critical settings
- vscode.window.showWarningMessage(
- 'You have changed a critical setting for the EcoOptimizer plugin. Ensure the new value is valid and correct for optimal functionality.',
- );
- }
+ ecoOutput.info('Eco-Optimizer extension activated successfully');
+ console.log('Eco-Optimizer extension activated successfully');
}
export function deactivate(): void {
- console.log('Eco: Deactivating Plugin - Stopping Log Watching');
- stopWatchingLogs();
+ ecoOutput.info('Extension deactivated');
+ console.log('Extension deactivated');
+
+ // server.dispose();
+ backendLogManager.stopWatchingLogs();
+ ecoOutput.dispose();
}
diff --git a/src/global.d.ts b/src/global.d.ts
index cf5f436..3371cf5 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -1,65 +1,99 @@
export {};
-// global.d.ts
-export {};
-
+/**
+ * Global type declarations for the Eco-Optimizer extension.
+ * These interfaces define the core data structures used throughout the application.
+ */
declare global {
- // Define your global types here
+ /**
+ * Represents a specific location in source code where a smell occurs.
+ * Uses VS Code-style line/column numbering (1-based).
+ */
export interface Occurrence {
+ /** The starting line number (1-based) */
line: number;
+ /** The ending line number (1-based, optional) */
endLine?: number;
+ /** The starting column number (1-based) */
column: number;
+ /** The ending column number (1-based, optional) */
endColumn?: number;
}
+ /**
+ * Additional context-specific information about a code smell.
+ * The fields vary depending on the smell type.
+ */
export interface AdditionalInfo {
- // CRC
+ // Fields for Cached Repeated Calls (CRC) smell:
+ /** Number of repetitions found (for CRC smells) */
repetitions?: number;
+ /** The call string that's being repeated (for CRC smells) */
callString?: string;
- // SCL
+
+ // Fields for String Concatenation in Loop (SCL) smell:
+ /** The target variable being concatenated (for SCL smells) */
concatTarget?: string;
+ /** The line number where the inner loop occurs (for SCL smells) */
innerLoopLine?: number;
}
+ /**
+ * Represents a detected code smell with all its metadata.
+ * This is the core data structure for analysis results.
+ */
export interface Smell {
- type: string; // Type of the smell (e.g., "performance", "convention")
- symbol: string; // Symbolic identifier for the smell (e.g., "cached-repeated-calls")
- message: string; // Detailed description of the smell
- messageId: string; // Unique ID for the smell
- confidence: string; // Confidence level (e.g., "HIGH", "MEDIUM")
- path: string; // Optional: absolute file path
- module: string; // Optional: Module name
- obj?: string; // Optional: Object name associated with the smell (if applicable)
- occurences: Occurrence[]; // Optional: List of occurrences for repeated calls
+ /** Category of the smell (e.g., "performance", "convention") */
+ type: string;
+ /** Unique identifier for the smell type (e.g., "cached-repeated-calls") */
+ symbol: string;
+ /** Human-readable description of the smell */
+ message: string;
+ /** Unique message ID for specific smell variations */
+ messageId: string;
+ /** Confidence level in detection ("HIGH", "MEDIUM", "LOW") */
+ confidence: string;
+ /** Absolute path to the file containing the smell */
+ path: string;
+ /** Module or namespace where the smell was found */
+ module: string;
+ /** Specific object/function name (when applicable) */
+ obj?: string;
+ /** All detected locations of this smell in the code */
+ occurences: Occurrence[];
+ /** Type-specific additional information about the smell */
additionalInfo: AdditionalInfo;
+ /** Unique identifier for this specific smell instance */
+ id?: string;
}
-
- export interface ChangedFile {
- original: string;
- refactored: string;
- }
-
+
+ /**
+ * Represents the response from the backend refactoring service.
+ * Contains all necessary information to present and apply refactorings.
+ */
export interface RefactoredData {
+ /** Temporary directory containing all refactored files */
tempDir: string;
- targetFile: ChangedFile;
- energySaved: number;
- affectedFiles: ChangedFile[];
+ /** The main file that was refactored */
+ targetFile: {
+ /** Path to the original version */
+ original: string;
+ /** Path to the refactored version */
+ refactored: string;
+ };
+ /** Estimated energy savings in joules (optional) */
+ energySaved?: number;
+ /** Any additional files affected by the refactoring */
+ affectedFiles: {
+ /** Path to the original version */
+ original: string;
+ /** Path to the refactored version */
+ refactored: string;
+ }[];
}
-
- export interface RefactorOutput {
- refactoredData?: RefactoredData; // Refactored code as a string
- updatedSmells: Smell[]; //
- }
-
- export interface ActiveDiff {
- files: ChangedFile[];
- isOpen: boolean;
- firstOpen: boolean;
+
+ export interface RefactorArtifacts {
+ refactoredData: RefactoredData;
+ smell: Smell;
}
-
- export type SmellDetails = {
- symbol: string;
- message: string;
- };
-
-}
+}
\ No newline at end of file
diff --git a/src/install.ts b/src/install.ts
new file mode 100644
index 0000000..f018d07
--- /dev/null
+++ b/src/install.ts
@@ -0,0 +1,310 @@
+import * as path from 'path';
+import * as childProcess from 'child_process';
+import { access, unlink, writeFile } from 'fs/promises';
+
+// Constants for package management
+const PACKAGE_NAME = 'ecooptimizer';
+const PYPI_INDEX = 'https://pypi.org/simple';
+
+/**
+ * Configuration interface for Python environment setup
+ */
+interface InstallConfig {
+ pythonPath: string;
+ version: string;
+ targetDir: string;
+}
+
+/**
+ * Ensures a valid Python virtual environment exists
+ * @param config Installation configuration
+ * @returns Path to the Python executable in the virtual environment
+ * @throws Error if environment setup fails
+ */
+async function ensurePythonEnvironment(config: InstallConfig): Promise {
+ const venvPath = path.join(config.targetDir, '.venv');
+ const isWindows = process.platform === 'win32';
+ const pythonExecutable = path.join(
+ venvPath,
+ isWindows ? 'Scripts' : 'bin',
+ isWindows ? 'python.exe' : 'python',
+ );
+
+ try {
+ // 1. Verify Python is available and executable
+ await new Promise((resolve, reject) => {
+ const pythonCheck = childProcess.spawn(config.pythonPath, ['--version']);
+ pythonCheck.stderr?.on('data', (chunk) => console.error(chunk));
+ pythonCheck.stdout?.on('data', (chunk) => console.log(chunk));
+ pythonCheck.on('close', (code) => {
+ code === 0
+ ? resolve()
+ : reject(new Error(`Python check failed (code ${code})`));
+ });
+ pythonCheck.on('error', (err) => {
+ console.error(err);
+ reject(err);
+ });
+ });
+
+ // 2. Check for existing virtual environment
+ let venvExists = false;
+ try {
+ await access(venvPath);
+ venvExists = true;
+ console.log('Virtual environment already exists');
+ } catch {
+ console.log('Creating virtual environment...');
+ }
+
+ if (!venvExists) {
+ const tempFile = path.join(config.targetDir, 'create_venv_temp.py');
+ try {
+ // Python script to create virtual environment
+ const scriptContent = `
+import sys
+import venv
+import os
+
+try:
+ venv.create("${venvPath.replace(/\\/g, '\\\\')}",
+ clear=True,
+ with_pip=True)
+ print("VENV_CREATION_SUCCESS")
+except Exception as e:
+ print(f"VENV_CREATION_ERROR: {str(e)}", file=sys.stderr)
+ sys.exit(1)
+`;
+ await writeFile(tempFile, scriptContent);
+
+ const creationSuccess = await new Promise((resolve, reject) => {
+ const proc = childProcess.spawn(config.pythonPath, [tempFile], {
+ stdio: 'pipe',
+ });
+
+ let output = '';
+ let errorOutput = '';
+
+ proc.stdout.on('data', (data) => (output += data.toString()));
+ proc.stderr.on('data', (data) => (errorOutput += data.toString()));
+
+ proc.on('close', (code) => {
+ if (code === 0 && output.includes('VENV_CREATION_SUCCESS')) {
+ resolve(true);
+ } else {
+ const errorMatch = errorOutput.match(/VENV_CREATION_ERROR: (.+)/);
+ const errorMessage =
+ errorMatch?.[1] || `Process exited with code ${code}`;
+ console.error('Virtual environment creation failed:', errorMessage);
+ reject(new Error(errorMessage));
+ }
+ });
+
+ proc.on('error', (err) => {
+ console.error('Process error:', err);
+ reject(err);
+ });
+ });
+
+ // Fallback check if venv was partially created
+ if (!creationSuccess) {
+ try {
+ await access(pythonExecutable);
+ console.warn('Using partially created virtual environment');
+ } catch (accessError) {
+ console.error(
+ 'Partial virtual environment creation failed:',
+ accessError,
+ );
+ throw new Error('Virtual environment creation completely failed');
+ }
+ }
+ } finally {
+ // Clean up temporary file
+ await unlink(tempFile).catch(() => {});
+ }
+ }
+
+ // 3. Final verification of virtual environment Python
+ await access(pythonExecutable);
+ return pythonExecutable;
+ } catch (error: any) {
+ console.error('Error in ensurePythonEnvironment:', error.message);
+ throw error;
+ }
+}
+
+/**
+ * Verifies installed package version matches expected version
+ * @param pythonPath Path to Python executable
+ * @param config Installation configuration
+ * @returns true if version matches
+ * @throws Error if version mismatch or package not found
+ */
+async function verifyPyPackage(
+ pythonPath: string,
+ config: InstallConfig,
+): Promise {
+ console.log('Verifying python package version...');
+ const installedVersion = childProcess
+ .execSync(
+ `"${pythonPath}" -c "import importlib.metadata; print(importlib.metadata.version('${PACKAGE_NAME}'))"`,
+ )
+ .toString()
+ .trim();
+
+ if (installedVersion !== config.version) {
+ throw new Error(
+ `Version mismatch: Expected ${config.version}, got ${installedVersion}`,
+ );
+ }
+
+ console.log('Version match.');
+ return true;
+}
+
+/**
+ * Installs package from PyPI into virtual environment
+ * @param config Installation configuration
+ */
+async function installFromPyPI(config: InstallConfig): Promise {
+ let pythonPath: string;
+ try {
+ pythonPath = await ensurePythonEnvironment(config);
+ console.log('Python environment is ready at:', pythonPath);
+ } catch (error: any) {
+ console.error('Failed to set up Python environment:', error.message);
+ return;
+ }
+ const pipPath = pythonPath.replace('python', 'pip');
+
+ // Skip if already installed
+ if (await verifyPyPackage(pythonPath, config)) {
+ console.log('Package already installed.');
+ return;
+ }
+
+ // Update setuptools first
+ console.log('Installing setup tools...');
+ try {
+ childProcess.execSync(`"${pipPath}" install --upgrade "setuptools>=45.0.0"`, {
+ stdio: 'inherit',
+ });
+ } catch (error) {
+ console.warn('Could not update setuptools:', error);
+ }
+
+ // Main package installation
+ console.log('Installing ecooptimizer...');
+ try {
+ childProcess.execSync(
+ `"${pipPath}" install --index-url ${PYPI_INDEX} "${PACKAGE_NAME}==${config.version}"`,
+ { stdio: 'inherit' },
+ );
+
+ verifyPyPackage(pythonPath, config);
+ console.log('β
Installation completed successfully');
+ } catch (error) {
+ console.error('β Installation failed:', error);
+ throw error;
+ }
+}
+
+/**
+ * Finds a valid Python executable path
+ * @returns Path to Python executable
+ * @throws Error if no valid Python found
+ */
+async function findPythonPath(): Promise {
+ // Check explicit environment variable first
+ if (process.env.PYTHON_PATH && (await validatePython(process.env.PYTHON_PATH))) {
+ return process.env.PYTHON_PATH;
+ }
+
+ // Common Python executable names (ordered by preference)
+ const candidates = ['python', 'python3.10', 'python3', 'py'];
+
+ // Platform-specific locations
+ if (process.platform === 'win32') {
+ candidates.push(
+ path.join(
+ process.env.LOCALAPPDATA || '',
+ 'Programs',
+ 'Python',
+ 'Python310',
+ 'python.exe',
+ ),
+ path.join(process.env.ProgramFiles || '', 'Python310', 'python.exe'),
+ );
+ }
+
+ if (process.platform === 'darwin') {
+ candidates.push('/usr/local/bin/python3'); // Homebrew default
+ }
+
+ // Check environment-specific paths
+ if (process.env.CONDA_PREFIX) {
+ candidates.push(path.join(process.env.CONDA_PREFIX, 'bin', 'python'));
+ }
+
+ if (process.env.VIRTUAL_ENV) {
+ candidates.push(path.join(process.env.VIRTUAL_ENV, 'bin', 'python'));
+ }
+
+ // Test each candidate
+ for (const candidate of candidates) {
+ try {
+ if (await validatePython(candidate)) {
+ return candidate;
+ }
+ } catch {
+ continue;
+ }
+ }
+
+ throw new Error('No valid Python installation found');
+}
+
+/**
+ * Validates Python executable meets requirements
+ * @param pythonPath Path to Python executable
+ * @returns true if valid Python 3.9+ installation
+ */
+async function validatePython(pythonPath: string): Promise {
+ try {
+ const versionOutput = childProcess
+ .execSync(`"${pythonPath}" --version`)
+ .toString()
+ .trim();
+
+ const versionMatch = versionOutput.match(/Python (\d+)\.(\d+)/);
+ if (!versionMatch) return false;
+
+ const major = parseInt(versionMatch[1]);
+ const minor = parseInt(versionMatch[2]);
+
+ console.log('Python version:', major, minor);
+ return major === 3 && minor >= 9; // Require Python 3.9+
+ } catch {
+ return false;
+ }
+}
+
+// Main execution block when run directly
+if (require.main === module) {
+ (async (): Promise => {
+ try {
+ const config: InstallConfig = {
+ pythonPath: await findPythonPath(),
+ version: require('../package.json').version,
+ targetDir: process.cwd(),
+ };
+
+ console.log(`Using Python at: ${config.pythonPath}`);
+ await installFromPyPI(config);
+ } catch (error) {
+ console.error('Fatal error:', error instanceof Error ? error.message : error);
+ process.exit(1);
+ }
+ })();
+}
diff --git a/src/lib/README.md b/src/lib/README.md
new file mode 100644
index 0000000..94ac452
--- /dev/null
+++ b/src/lib/README.md
@@ -0,0 +1,3 @@
+The files in this folder do not currently do anything in the extension.
+
+They are left here for the future when the server backend can be properly integrated into the extension.
\ No newline at end of file
diff --git a/src/lib/dependencyManager.ts b/src/lib/dependencyManager.ts
new file mode 100644
index 0000000..a388d6e
--- /dev/null
+++ b/src/lib/dependencyManager.ts
@@ -0,0 +1,77 @@
+import { existsSync } from 'fs';
+import { join } from 'path';
+import * as vscode from 'vscode';
+import childProcess from 'child_process';
+
+/**
+ * Handles Python dependency management for the extension.
+ * Creates and manages a virtual environment (.venv) in the extension directory
+ * and provides installation capabilities when dependencies are missing.
+ */
+export class DependencyManager {
+ /**
+ * Ensures required dependencies are installed. Checks for existing virtual environment
+ * and prompts user to install if missing.
+ *
+ * @param context - Extension context containing installation path
+ * @returns Promise resolving to true if dependencies are available, false otherwise
+ */
+ static async ensureDependencies(
+ context: vscode.ExtensionContext,
+ ): Promise {
+ // Check for existing virtual environment
+ const venvPath = join(context.extensionPath, '.venv');
+ if (existsSync(venvPath)) return true;
+
+ // Prompt user to install dependencies if venv doesn't exist
+ const choice = await vscode.window.showErrorMessage(
+ 'Python dependencies missing. Install now?',
+ 'Install',
+ 'Cancel',
+ );
+
+ if (choice === 'Install') {
+ return vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Notification,
+ title: 'Installing dependencies...',
+ },
+ async () => {
+ try {
+ await this.runInstaller(context);
+ return true;
+ } catch (error) {
+ vscode.window.showErrorMessage(`Installation failed: ${error}`);
+ return false;
+ }
+ },
+ );
+ }
+ return false;
+ }
+
+ /**
+ * Executes the dependency installation process in a child process.
+ * Shows progress to user and handles installation errors.
+ *
+ * @param context - Extension context containing installation path
+ * @throws Error when installation process fails
+ */
+ private static async runInstaller(
+ context: vscode.ExtensionContext,
+ ): Promise {
+ return new Promise((resolve, reject) => {
+ // Spawn installer process with inherited stdio for live output
+ const installer = childProcess.spawn('node', ['dist/install.js'], {
+ cwd: context.extensionPath,
+ stdio: 'inherit', // Show installation progress in parent console
+ });
+
+ installer.on('close', (code) =>
+ code === 0
+ ? resolve()
+ : reject(new Error(`Installer exited with code ${code}`)),
+ );
+ });
+ }
+}
diff --git a/src/lib/processManager.ts b/src/lib/processManager.ts
new file mode 100644
index 0000000..37f66eb
--- /dev/null
+++ b/src/lib/processManager.ts
@@ -0,0 +1,134 @@
+import * as childProcess from 'child_process';
+import { existsSync } from 'fs';
+import * as net from 'net';
+import { join } from 'path';
+import * as vscode from 'vscode';
+import { ecoOutput } from '../extension';
+
+/**
+ * Manages the lifecycle of the backend server process, including:
+ * - Starting the Python server with proper environment
+ * - Port allocation and verification
+ * - Process cleanup on exit
+ * - Logging and error handling
+ */
+export class ServerProcess {
+ private process?: childProcess.ChildProcess;
+
+ constructor(private context: vscode.ExtensionContext) {}
+
+ /**
+ * Starts the backend server process and verifies it's ready.
+ * @returns Promise resolving to the port number the server is running on
+ * @throws Error if server fails to start or Python environment is missing
+ */
+ async start(): Promise {
+ // Determine Python executable path based on platform
+ const pythonPath = join(
+ this.context.extensionPath,
+ process.platform === 'win32'
+ ? '.venv\\Scripts\\python.exe'
+ : '.venv/bin/python',
+ );
+
+ if (!existsSync(pythonPath)) {
+ throw new Error('Python environment not found');
+ }
+
+ // Clean up any existing server process
+ await this.killProcessTree();
+
+ // Find and bind to an available port
+ const port = await this.findFreePort();
+
+ // Start the Python server process
+ this.process = childProcess.spawn(
+ pythonPath,
+ ['-m', 'ecooptimizer.api', '--port', port.toString(), '--dev'],
+ {
+ cwd: this.context.extensionPath,
+ env: { ...process.env, PYTHONUNBUFFERED: '1' }, // Ensure unbuffered output
+ },
+ );
+
+ // Set up process event handlers
+ this.process.stdout?.on('data', (data) => ecoOutput.info(`[Server] ${data}`));
+ this.process.stderr?.on('data', (data) => ecoOutput.error(`[Server] ${data}`));
+ this.process.on('close', () => {
+ ecoOutput.info('Server stopped');
+ console.log('Server stopped');
+ });
+
+ // Verify server is actually listening before returning
+ await this.verifyReady(port);
+ return port;
+ }
+
+ /**
+ * Finds an available network port
+ * @returns Promise resolving to an available port number
+ */
+ private async findFreePort(): Promise {
+ return new Promise((resolve) => {
+ const server = net.createServer();
+ server.listen(0, () => {
+ const port = (server.address() as net.AddressInfo).port;
+ server.close(() => resolve(port));
+ });
+ });
+ }
+
+ /**
+ * Kills the server process and its entire process tree
+ * Handles platform-specific process termination
+ */
+ private async killProcessTree(): Promise {
+ if (!this.process?.pid) return;
+
+ try {
+ if (process.platform === 'win32') {
+ // Windows requires taskkill for process tree termination
+ childProcess.execSync(`taskkill /PID ${this.process.pid} /T /F`);
+ } else {
+ // Unix systems can kill process groups with negative PID
+ process.kill(-this.process.pid, 'SIGKILL');
+ }
+ } catch (error) {
+ ecoOutput.error(`Process cleanup failed: ${error}`);
+ } finally {
+ this.process = undefined;
+ }
+ }
+
+ /**
+ * Verifies the server is actually listening on the specified port
+ * @param port Port number to check
+ * @param timeout Maximum wait time in milliseconds
+ * @throws Error if server doesn't become ready within timeout
+ */
+ private async verifyReady(port: number, timeout = 10000): Promise {
+ const start = Date.now();
+ while (Date.now() - start < timeout) {
+ try {
+ const socket = net.createConnection({ port });
+ await new Promise((resolve, reject) => {
+ socket.on('connect', resolve);
+ socket.on('error', reject);
+ });
+ socket.end();
+ return;
+ } catch {
+ // Retry after short delay if connection fails
+ await new Promise((resolve) => setTimeout(resolve, 200));
+ }
+ }
+ throw new Error(`Server didn't start within ${timeout}ms`);
+ }
+
+ /**
+ * Clean up resources when disposing of the manager
+ */
+ dispose(): void {
+ this.process?.kill();
+ }
+}
diff --git a/src/listeners/workspaceModifiedListener.ts b/src/listeners/workspaceModifiedListener.ts
new file mode 100644
index 0000000..14eb386
--- /dev/null
+++ b/src/listeners/workspaceModifiedListener.ts
@@ -0,0 +1,201 @@
+import * as vscode from 'vscode';
+import { basename } from 'path';
+
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
+import { SmellsViewProvider } from '../providers/SmellsViewProvider';
+import { MetricsViewProvider } from '../providers/MetricsViewProvider';
+import { ecoOutput, isSmellLintingEnabled } from '../extension';
+import { detectSmellsFile } from '../commands/detection/detectSmells';
+import { envConfig } from '../utils/envConfig';
+
+/**
+ * Monitors workspace modifications and maintains analysis state consistency by:
+ * - Tracking file system changes (create/change/delete)
+ * - Handling document save events
+ * - Managing cache invalidation
+ * - Coordinating view updates
+ */
+export class WorkspaceModifiedListener {
+ private fileWatcher: vscode.FileSystemWatcher | undefined;
+ private saveListener: vscode.Disposable | undefined;
+
+ constructor(
+ private context: vscode.ExtensionContext,
+ private smellsCacheManager: SmellsCacheManager,
+ private smellsViewProvider: SmellsViewProvider,
+ private metricsViewProvider: MetricsViewProvider,
+ ) {
+ this.initializeFileWatcher();
+ this.initializeSaveListener();
+ ecoOutput.trace(
+ '[WorkspaceListener] Initialized workspace modification listener',
+ );
+ }
+
+ /**
+ * Creates file system watcher for Python files in configured workspace
+ */
+ private initializeFileWatcher(): void {
+ const configuredPath = this.context.workspaceState.get(
+ envConfig.WORKSPACE_CONFIGURED_PATH!,
+ );
+ if (!configuredPath) {
+ ecoOutput.trace(
+ '[WorkspaceListener] No workspace configured - skipping file watcher',
+ );
+ return;
+ }
+
+ try {
+ this.fileWatcher = vscode.workspace.createFileSystemWatcher(
+ new vscode.RelativePattern(configuredPath, '**/*.py'),
+ false, // Watch create events
+ false, // Watch change events
+ false, // Watch delete events
+ );
+
+ this.fileWatcher.onDidCreate(() => {
+ ecoOutput.trace('[WorkspaceListener] Detected new Python file');
+ this.refreshViews();
+ });
+
+ this.fileWatcher.onDidDelete((uri) => {
+ ecoOutput.trace(`[WorkspaceListener] Detected deletion of ${uri.fsPath}`);
+ this.handleFileDeletion(uri.fsPath);
+ });
+
+ ecoOutput.trace(
+ `[WorkspaceListener] Watching Python files in ${configuredPath}`,
+ );
+ } catch (error) {
+ ecoOutput.error(
+ `[WorkspaceListener] Error initializing file watcher: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ }
+ }
+
+ /**
+ * Sets up document save listener for Python files
+ */
+ private initializeSaveListener(): void {
+ this.saveListener = vscode.workspace.onDidSaveTextDocument((document) => {
+ if (document.languageId === 'python') {
+ ecoOutput.trace(
+ `[WorkspaceListener] Detected save in ${document.uri.fsPath}`,
+ );
+ this.handleFileChange(document.uri.fsPath);
+
+ if (isSmellLintingEnabled()) {
+ ecoOutput.info(
+ `[WorkspaceListener] Smell linting is ON β auto-detecting smells for ${document.uri.fsPath}`,
+ );
+ detectSmellsFile(
+ document.uri.fsPath,
+ this.smellsViewProvider,
+ this.smellsCacheManager,
+ );
+ }
+ }
+ });
+ }
+
+ /**
+ * Handles file modifications by:
+ * - Invalidating cached analysis if exists
+ * - Marking file as outdated in UI
+ * @param filePath - Absolute path to modified file
+ */
+ private async handleFileChange(filePath: string): Promise {
+ // Log current cache state for debugging
+ const cachedFiles = this.smellsCacheManager.getAllFilePaths();
+ ecoOutput.trace(
+ `[WorkspaceListener] Current cached files (${cachedFiles.length}):\n` +
+ cachedFiles.map((f) => ` - ${f}`).join('\n'),
+ );
+
+ const hadCache = this.smellsCacheManager.hasFileInCache(filePath);
+ if (!hadCache) {
+ ecoOutput.trace(`[WorkspaceListener] No cache to invalidate for ${filePath}`);
+ return;
+ }
+
+ try {
+ await this.smellsCacheManager.clearCachedSmellsForFile(filePath);
+ this.smellsViewProvider.setStatus(filePath, 'outdated');
+
+ ecoOutput.trace(
+ `[WorkspaceListener] Invalidated cache for modified file: ${filePath}`,
+ );
+ vscode.window.showInformationMessage(
+ `Analysis data marked outdated for ${basename(filePath)}`,
+ { modal: false },
+ );
+
+ this.refreshViews();
+ } catch (error) {
+ ecoOutput.error(
+ `[WorkspaceListener] Error handling file change: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ }
+ }
+
+ /**
+ * Handles file deletions by:
+ * - Clearing related cache entries
+ * - Removing from UI views
+ * @param filePath - Absolute path to deleted file
+ */
+ private async handleFileDeletion(filePath: string): Promise {
+ const hadCache = this.smellsCacheManager.hasCachedSmells(filePath);
+ let removed = false;
+
+ if (hadCache) {
+ try {
+ await this.smellsCacheManager.clearCachedSmellsByPath(filePath);
+ removed = true;
+ ecoOutput.trace(
+ `[WorkspaceListener] Cleared cache for deleted file: ${filePath}`,
+ );
+ } catch (error) {
+ ecoOutput.error(
+ `[WorkspaceListener] Error clearing cache: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ }
+ }
+
+ const removedFromTree = this.smellsViewProvider.removeFile(filePath);
+ if (removedFromTree) {
+ removed = true;
+ ecoOutput.trace(`[WorkspaceListener] Removed from view: ${filePath}`);
+ }
+
+ if (removed) {
+ vscode.window.showInformationMessage(
+ `Removed analysis data for deleted file: ${basename(filePath)}`,
+ { modal: false },
+ );
+ }
+
+ this.refreshViews();
+ }
+
+ /**
+ * Triggers refresh of all dependent views
+ */
+ private refreshViews(): void {
+ this.smellsViewProvider.refresh();
+ this.metricsViewProvider.refresh();
+ ecoOutput.trace('[WorkspaceListener] Refreshed all views');
+ }
+
+ /**
+ * Cleans up resources including:
+ * - File system watcher
+ * - Document save listener
+ */
+ public dispose(): void {
+ this.fileWatcher?.dispose();
+ this.saveListener?.dispose();
+ ecoOutput.trace('[WorkspaceListener] Disposed all listeners');
+ }
+}
diff --git a/src/providers/FilterViewProvider.ts b/src/providers/FilterViewProvider.ts
new file mode 100644
index 0000000..cab9a17
--- /dev/null
+++ b/src/providers/FilterViewProvider.ts
@@ -0,0 +1,269 @@
+import * as vscode from 'vscode';
+import {
+ FilterSmellConfig,
+ getFilterSmells,
+ loadSmells,
+ saveSmells,
+} from '../utils/smellsData';
+import { MetricsViewProvider } from './MetricsViewProvider';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
+import { SmellsViewProvider } from './SmellsViewProvider';
+
+/**
+ * Provides a tree view for managing and filtering code smells in the VS Code extension.
+ * Handles smell configuration, option editing, and maintains consistency with cached results.
+ */
+export class FilterViewProvider implements vscode.TreeDataProvider {
+ // Event emitter for tree view updates
+ private _onDidChangeTreeData: vscode.EventEmitter<
+ vscode.TreeItem | undefined | void
+ > = new vscode.EventEmitter();
+ readonly onDidChangeTreeData: vscode.Event =
+ this._onDidChangeTreeData.event;
+
+ private treeView?: vscode.TreeView;
+ private smells: Record = {};
+
+ constructor(
+ private context: vscode.ExtensionContext,
+ private metricsViewProvider: MetricsViewProvider,
+ private smellsCacheManager: SmellsCacheManager,
+ private smellsViewProvider: SmellsViewProvider,
+ ) {
+ this.smells = getFilterSmells();
+ }
+
+ /**
+ * Sets up the tree view and handles checkbox state changes
+ * @param treeView The VS Code tree view instance to manage
+ */
+ setTreeView(treeView: vscode.TreeView): void {
+ this.treeView = treeView;
+
+ this.treeView.onDidChangeCheckboxState(async (event) => {
+ for (const [item] of event.items) {
+ if (item instanceof SmellItem) {
+ const confirmed = await this.confirmFilterChange();
+ if (confirmed) {
+ await this.toggleSmell(item.key);
+ } else {
+ // Refresh view if change was cancelled
+ this._onDidChangeTreeData.fire();
+ }
+ }
+ }
+ });
+ }
+
+ getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
+ return element;
+ }
+
+ /**
+ * Gets children items for the tree view
+ * @param element Parent element or undefined for root items
+ * @returns Promise resolving to array of tree items
+ */
+ getChildren(element?: SmellItem): Thenable {
+ if (!element) {
+ // Root level items - all available smells
+ return Promise.resolve(
+ Object.keys(this.smells)
+ .sort((a, b) => this.smells[a].name.localeCompare(this.smells[b].name))
+ .map((smellKey) => {
+ const smell = this.smells[smellKey];
+ return new SmellItem(
+ smellKey,
+ smell.name,
+ smell.enabled,
+ smell.analyzer_options &&
+ Object.keys(smell.analyzer_options).length > 0
+ ? vscode.TreeItemCollapsibleState.Collapsed
+ : vscode.TreeItemCollapsibleState.None,
+ );
+ }),
+ );
+ }
+
+ // Child items - smell configuration options
+ const options = this.smells[element.key]?.analyzer_options;
+ return options
+ ? Promise.resolve(
+ Object.entries(options).map(
+ ([optionKey, optionData]) =>
+ new SmellOptionItem(
+ optionKey,
+ optionData.label,
+ optionData.value,
+ optionData.description,
+ element.key,
+ ),
+ ),
+ )
+ : Promise.resolve([]);
+ }
+
+ /**
+ * Toggles a smell's enabled state
+ * @param smellKey The key of the smell to toggle
+ */
+ async toggleSmell(smellKey: string): Promise {
+ if (this.smells[smellKey]) {
+ this.smells[smellKey].enabled = !this.smells[smellKey].enabled;
+ saveSmells(this.smells);
+ await this.invalidateCachedSmellsForAffectedFiles();
+ this._onDidChangeTreeData.fire();
+ }
+ }
+
+ /**
+ * Updates a smell analyzer option value
+ * @param smellKey The smell containing the option
+ * @param optionKey The option to update
+ * @param newValue The new value for the option
+ */
+ async updateOption(
+ smellKey: string,
+ optionKey: string,
+ newValue: number | string,
+ ): Promise {
+ const confirmed = await this.confirmFilterChange();
+ if (!confirmed) return;
+
+ if (this.smells[smellKey]?.analyzer_options?.[optionKey]) {
+ this.smells[smellKey].analyzer_options[optionKey].value = newValue;
+ saveSmells(this.smells);
+ await this.invalidateCachedSmellsForAffectedFiles();
+ this._onDidChangeTreeData.fire();
+ } else {
+ vscode.window.showErrorMessage(
+ `Error: No analyzer option found for ${optionKey}`,
+ );
+ }
+ }
+
+ refresh(): void {
+ this._onDidChangeTreeData.fire(undefined);
+ }
+
+ /**
+ * Enables or disables all smells at once
+ * @param enabled Whether to enable or disable all smells
+ */
+ async setAllSmellsEnabled(enabled: boolean): Promise {
+ const confirmed = await this.confirmFilterChange();
+ if (!confirmed) return;
+
+ Object.keys(this.smells).forEach((key) => {
+ this.smells[key].enabled = enabled;
+ });
+ saveSmells(this.smells);
+ await this.invalidateCachedSmellsForAffectedFiles();
+ this._onDidChangeTreeData.fire();
+ }
+
+ /**
+ * Resets all smell configurations to their default values
+ */
+ async resetToDefaults(): Promise {
+ const confirmed = await this.confirmFilterChange();
+ if (!confirmed) return;
+
+ loadSmells('default');
+ this.smells = getFilterSmells();
+ saveSmells(this.smells);
+
+ await this.invalidateCachedSmellsForAffectedFiles();
+ this._onDidChangeTreeData.fire();
+ }
+
+ /**
+ * Invalidates cached smells for all files when filters change
+ */
+ async invalidateCachedSmellsForAffectedFiles(): Promise {
+ const cachedFilePaths = this.smellsCacheManager.getAllFilePaths();
+
+ for (const filePath of cachedFilePaths) {
+ this.smellsCacheManager.clearCachedSmellsForFile(filePath);
+ this.smellsViewProvider.setStatus(filePath, 'outdated');
+ }
+
+ this.metricsViewProvider.refresh();
+ this.smellsViewProvider.refresh();
+ }
+
+ /**
+ * Shows confirmation dialog for filter changes that invalidate cache
+ * @returns Promise resolving to whether change should proceed
+ */
+ private async confirmFilterChange(): Promise {
+ const suppressWarning = this.context.workspaceState.get(
+ 'ecooptimizer.suppressFilterWarning',
+ false,
+ );
+
+ if (suppressWarning) {
+ return true;
+ }
+
+ const result = await vscode.window.showWarningMessage(
+ 'Changing smell filters will invalidate existing analysis results. Do you want to continue?',
+ { modal: true },
+ 'Yes',
+ "Don't Remind Me Again",
+ );
+
+ if (result === "Don't Remind Me Again") {
+ await this.context.workspaceState.update(
+ 'ecooptimizer.suppressFilterWarning',
+ true,
+ );
+ return true;
+ }
+
+ return result === 'Yes';
+ }
+}
+
+/**
+ * Tree item representing a single smell in the filter view
+ */
+class SmellItem extends vscode.TreeItem {
+ constructor(
+ public readonly key: string,
+ public readonly name: string,
+ public enabled: boolean,
+ public readonly collapsibleState: vscode.TreeItemCollapsibleState,
+ ) {
+ super(name, collapsibleState);
+ this.contextValue = 'smellItem';
+ this.checkboxState = enabled
+ ? vscode.TreeItemCheckboxState.Checked
+ : vscode.TreeItemCheckboxState.Unchecked;
+ }
+}
+
+/**
+ * Tree item representing a configurable option for a smell
+ */
+class SmellOptionItem extends vscode.TreeItem {
+ constructor(
+ public readonly optionKey: string,
+ public readonly label: string,
+ public value: number | string,
+ public readonly description: string,
+ public readonly smellKey: string,
+ ) {
+ super('placeholder', vscode.TreeItemCollapsibleState.None);
+
+ this.contextValue = 'smellOption';
+ this.label = `${label}: ${value}`;
+ this.tooltip = description;
+ this.description = '';
+ this.command = {
+ command: 'ecooptimizer.editSmellFilterOption',
+ title: 'Edit Option',
+ arguments: [this],
+ };
+ }
+}
diff --git a/src/providers/MetricsViewProvider.ts b/src/providers/MetricsViewProvider.ts
new file mode 100644
index 0000000..e003054
--- /dev/null
+++ b/src/providers/MetricsViewProvider.ts
@@ -0,0 +1,421 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import { basename, dirname } from 'path';
+import { buildPythonTree } from '../utils/TreeStructureBuilder';
+import { envConfig } from '../utils/envConfig';
+import { getFilterSmells } from '../utils/smellsData';
+import { normalizePath } from '../utils/normalizePath';
+
+/**
+ * Custom TreeItem for displaying metrics in the VS Code explorer
+ * Handles different node types (folders, files, smells) with appropriate icons and behaviors
+ */
+class MetricTreeItem extends vscode.TreeItem {
+ constructor(
+ public readonly label: string,
+ public readonly collapsibleState: vscode.TreeItemCollapsibleState,
+ public readonly contextValue: string,
+ public readonly carbonSaved?: number,
+ public readonly resourceUri?: vscode.Uri,
+ public readonly smellName?: string,
+ ) {
+ super(label, collapsibleState);
+
+ // Set icon based on node type
+ switch (this.contextValue) {
+ case 'folder':
+ this.iconPath = new vscode.ThemeIcon('folder');
+ break;
+ case 'file':
+ this.iconPath = new vscode.ThemeIcon('file');
+ break;
+ case 'smell':
+ this.iconPath = new vscode.ThemeIcon('tag');
+ break;
+ case 'folder-stats':
+ this.iconPath = new vscode.ThemeIcon('graph');
+ break;
+ }
+
+ // Format carbon savings display
+ this.description =
+ carbonSaved !== undefined
+ ? `Carbon Saved: ${formatNumber(carbonSaved)} kg`
+ : '';
+ this.tooltip = smellName || this.description;
+
+ // Make files clickable to open them
+ if (resourceUri && contextValue === 'file') {
+ this.command = {
+ title: 'Open File',
+ command: 'vscode.open',
+ arguments: [resourceUri],
+ };
+ }
+ }
+}
+
+/**
+ * Interface for storing metrics data for individual files
+ */
+export interface MetricsDataItem {
+ totalCarbonSaved: number;
+ smellDistribution: {
+ [smell: string]: number;
+ };
+}
+
+/**
+ * Structure for aggregating metrics across folders
+ */
+interface FolderMetrics {
+ totalCarbonSaved: number;
+ smellDistribution: Map; // Map
+ children: {
+ files: Map; // Map
+ folders: Map; // Map
+ };
+}
+
+/**
+ * Provides a tree view of carbon savings metrics across the workspace
+ * Aggregates data by folder structure and smell types with caching for performance
+ */
+export class MetricsViewProvider implements vscode.TreeDataProvider {
+ private _onDidChangeTreeData = new vscode.EventEmitter<
+ MetricTreeItem | undefined
+ >();
+ readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ // Cache for folder metrics to avoid repeated calculations
+ private folderMetricsCache: Map = new Map();
+
+ constructor(private context: vscode.ExtensionContext) {}
+
+ /**
+ * Triggers a refresh of the tree view
+ */
+ refresh(): void {
+ this._onDidChangeTreeData.fire(undefined);
+ }
+
+ getTreeItem(element: MetricTreeItem): vscode.TreeItem {
+ return element;
+ }
+
+ /**
+ * Builds the tree view hierarchy
+ * @param element The parent element or undefined for root items
+ * @returns Promise resolving to child tree items
+ */
+ async getChildren(element?: MetricTreeItem): Promise {
+ const metricsData = this.context.workspaceState.get<{
+ [path: string]: MetricsDataItem;
+ }>(envConfig.WORKSPACE_METRICS_DATA!, {});
+
+ // Root level items
+ if (!element) {
+ const configuredPath = this.context.workspaceState.get(
+ envConfig.WORKSPACE_CONFIGURED_PATH!,
+ );
+ if (!configuredPath) return [];
+
+ // Show either single file or folder contents at root
+ const isDirectory =
+ fs.existsSync(configuredPath) && fs.statSync(configuredPath).isDirectory();
+ if (isDirectory) {
+ return [this.createFolderItem(configuredPath)];
+ } else {
+ return [this.createFileItem(configuredPath, metricsData)];
+ }
+ }
+
+ // Folder contents
+ if (element.contextValue === 'folder') {
+ const folderPath = element.resourceUri!.fsPath;
+ const folderMetrics = await this.calculateFolderMetrics(
+ folderPath,
+ metricsData,
+ );
+ const treeNodes = buildPythonTree(folderPath);
+
+ // Create folder statistics section
+ const folderStats = [
+ new MetricTreeItem(
+ `Total Carbon Saved: ${formatNumber(folderMetrics.totalCarbonSaved)} kg`,
+ vscode.TreeItemCollapsibleState.None,
+ 'folder-stats',
+ ),
+ ...Array.from(folderMetrics.smellDistribution.entries()).map(
+ ([acronym, [name, carbonSaved]]) =>
+ this.createSmellItem({ acronym, name, carbonSaved }),
+ ),
+ ].sort(compareTreeItems);
+
+ // Create folder contents listing
+ const contents = treeNodes.map((node) => {
+ return node.isFile
+ ? this.createFileItem(node.fullPath, metricsData)
+ : this.createFolderItem(node.fullPath);
+ });
+
+ return [...contents, ...folderStats];
+ }
+
+ // File smell breakdown
+ if (element.contextValue === 'file') {
+ const filePath = element.resourceUri!.fsPath;
+ const fileMetrics = this.calculateFileMetrics(filePath, metricsData);
+ return fileMetrics.smellData.map((data) => this.createSmellItem(data));
+ }
+
+ return [];
+ }
+
+ /**
+ * Creates a folder tree item
+ */
+ private createFolderItem(folderPath: string): MetricTreeItem {
+ return new MetricTreeItem(
+ basename(folderPath),
+ vscode.TreeItemCollapsibleState.Collapsed,
+ 'folder',
+ undefined,
+ vscode.Uri.file(folderPath),
+ );
+ }
+
+ /**
+ * Creates a file tree item with carbon savings
+ */
+ private createFileItem(
+ filePath: string,
+ metricsData: { [path: string]: MetricsDataItem },
+ ): MetricTreeItem {
+ const fileMetrics = this.calculateFileMetrics(filePath, metricsData);
+ return new MetricTreeItem(
+ basename(filePath),
+ vscode.TreeItemCollapsibleState.Collapsed,
+ 'file',
+ fileMetrics.totalCarbonSaved,
+ vscode.Uri.file(filePath),
+ );
+ }
+
+ /**
+ * Creates a smell breakdown item
+ */
+ private createSmellItem(data: {
+ acronym: string;
+ name: string;
+ carbonSaved: number;
+ }): MetricTreeItem {
+ return new MetricTreeItem(
+ `${data.acronym}: ${formatNumber(data.carbonSaved)} kg`,
+ vscode.TreeItemCollapsibleState.None,
+ 'smell',
+ undefined,
+ undefined,
+ data.name,
+ );
+ }
+
+ /**
+ * Calculates aggregated metrics for a folder and its contents
+ * Uses caching to optimize performance for large folder structures
+ */
+ private async calculateFolderMetrics(
+ folderPath: string,
+ metricsData: { [path: string]: MetricsDataItem },
+ ): Promise {
+ // Return cached metrics if available
+ const cachedMetrics = this.folderMetricsCache.get(folderPath);
+ if (cachedMetrics) {
+ return cachedMetrics;
+ }
+
+ const folderMetrics: FolderMetrics = {
+ totalCarbonSaved: 0,
+ smellDistribution: new Map(),
+ children: {
+ files: new Map(),
+ folders: new Map(),
+ },
+ };
+
+ // Build directory tree structure
+ const treeNodes = buildPythonTree(folderPath);
+
+ for (const node of treeNodes) {
+ if (node.isFile) {
+ // Aggregate file metrics
+ const fileMetrics = this.calculateFileMetrics(node.fullPath, metricsData);
+ folderMetrics.children.files.set(
+ node.fullPath,
+ fileMetrics.totalCarbonSaved,
+ );
+ folderMetrics.totalCarbonSaved += fileMetrics.totalCarbonSaved;
+
+ // Aggregate smell distribution from file
+ for (const smellData of fileMetrics.smellData) {
+ const current =
+ folderMetrics.smellDistribution.get(smellData.acronym)?.[1] || 0;
+ folderMetrics.smellDistribution.set(smellData.acronym, [
+ smellData.name,
+ current + smellData.carbonSaved,
+ ]);
+ }
+ } else {
+ // Recursively process subfolders
+ const subFolderMetrics = await this.calculateFolderMetrics(
+ node.fullPath,
+ metricsData,
+ );
+ folderMetrics.children.folders.set(node.fullPath, subFolderMetrics);
+ folderMetrics.totalCarbonSaved += subFolderMetrics.totalCarbonSaved;
+
+ // Aggregate smell distribution from subfolder
+ subFolderMetrics.smellDistribution.forEach(
+ ([name, carbonSaved], acronym) => {
+ const current = folderMetrics.smellDistribution.get(acronym)?.[1] || 0;
+ folderMetrics.smellDistribution.set(acronym, [
+ name,
+ current + carbonSaved,
+ ]);
+ },
+ );
+ }
+ }
+
+ // Cache the calculated metrics
+ this.folderMetricsCache.set(folderPath, folderMetrics);
+ return folderMetrics;
+ }
+
+ /**
+ * Calculates metrics for a single file
+ */
+ private calculateFileMetrics(
+ filePath: string,
+ metricsData: { [path: string]: MetricsDataItem },
+ ): {
+ totalCarbonSaved: number;
+ smellData: { acronym: string; name: string; carbonSaved: number }[];
+ } {
+ const smellConfigData = getFilterSmells();
+ const fileData = metricsData[normalizePath(filePath)] || {
+ totalCarbonSaved: 0,
+ smellDistribution: {},
+ };
+
+ // Filter smell distribution to only include enabled smells
+ const smellDistribution = Object.keys(smellConfigData).reduce(
+ (acc, symbol) => {
+ if (smellConfigData[symbol]) {
+ acc[symbol] = fileData.smellDistribution[symbol] || 0;
+ }
+ return acc;
+ },
+ {} as Record,
+ );
+
+ return {
+ totalCarbonSaved: fileData.totalCarbonSaved,
+ smellData: Object.entries(smellDistribution).map(([symbol, carbonSaved]) => ({
+ acronym: smellConfigData[symbol].acronym,
+ name: smellConfigData[symbol].name,
+ carbonSaved,
+ })),
+ };
+ }
+
+ /**
+ * Updates metrics for a file when new analysis results are available
+ */
+ updateMetrics(filePath: string, carbonSaved: number, smellSymbol: string): void {
+ const metrics = this.context.workspaceState.get<{
+ [path: string]: MetricsDataItem;
+ }>(envConfig.WORKSPACE_METRICS_DATA!, {});
+
+ const normalizedPath = normalizePath(filePath);
+
+ // Initialize metrics if they don't exist
+ if (!metrics[normalizedPath]) {
+ metrics[normalizedPath] = {
+ totalCarbonSaved: 0,
+ smellDistribution: {},
+ };
+ }
+
+ // Update metrics
+ metrics[normalizedPath].totalCarbonSaved =
+ (metrics[normalizedPath].totalCarbonSaved || 0) + carbonSaved;
+
+ if (!metrics[normalizedPath].smellDistribution[smellSymbol]) {
+ metrics[normalizedPath].smellDistribution[smellSymbol] = 0;
+ }
+ metrics[normalizedPath].smellDistribution[smellSymbol] += carbonSaved;
+
+ // Persist changes
+ this.context.workspaceState.update(envConfig.WORKSPACE_METRICS_DATA!, metrics);
+
+ // Clear cache for all parent folders
+ this.clearCacheForFileParents(filePath);
+ this.refresh();
+ }
+
+ /**
+ * Clears cached metrics for all parent folders of a modified file
+ */
+ private clearCacheForFileParents(filePath: string): void {
+ let configuredPath = this.context.workspaceState.get(
+ envConfig.WORKSPACE_CONFIGURED_PATH!,
+ );
+
+ if (!configuredPath) {
+ return;
+ }
+ configuredPath = normalizePath(configuredPath);
+
+ // Walk up the directory tree clearing cache
+ let currentPath = dirname(filePath);
+ while (currentPath.includes(configuredPath)) {
+ this.folderMetricsCache.delete(currentPath);
+ currentPath = dirname(currentPath);
+ }
+ }
+}
+
+// ===========================================================
+// HELPER FUNCTIONS
+// ===========================================================
+
+/**
+ * Priority for sorting tree items by type
+ */
+const contextPriority: { [key: string]: number } = {
+ folder: 1,
+ file: 2,
+ smell: 3,
+ 'folder-stats': 4,
+};
+
+/**
+ * Comparator for tree items (folders first, then files, then smells)
+ */
+function compareTreeItems(a: MetricTreeItem, b: MetricTreeItem): number {
+ const priorityA = contextPriority[a.contextValue] || 0;
+ const priorityB = contextPriority[b.contextValue] || 0;
+ if (priorityA !== priorityB) return priorityA - priorityB;
+ return a.label.localeCompare(b.label);
+}
+
+/**
+ * Formats numbers for display, using scientific notation for very small values
+ */
+function formatNumber(number: number, decimalPlaces: number = 2): string {
+ const threshold = 0.001;
+ return Math.abs(number) < threshold
+ ? number.toExponential(decimalPlaces)
+ : number.toFixed(decimalPlaces);
+}
diff --git a/src/providers/RefactoringDetailsViewProvider.ts b/src/providers/RefactoringDetailsViewProvider.ts
new file mode 100644
index 0000000..d5c8b45
--- /dev/null
+++ b/src/providers/RefactoringDetailsViewProvider.ts
@@ -0,0 +1,201 @@
+import * as vscode from 'vscode';
+import * as path from 'path';
+import { getDescriptionByMessageId, getNameByMessageId } from '../utils/smellsData';
+
+/**
+ * Provides a tree view that displays detailed information about ongoing refactoring operations.
+ * Shows the target smell, affected files, and estimated energy savings.
+ */
+export class RefactoringDetailsViewProvider
+ implements vscode.TreeDataProvider
+{
+ // Event emitter for tree data changes
+ private _onDidChangeTreeData = new vscode.EventEmitter<
+ RefactoringDetailItem | undefined
+ >();
+ readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ // State properties
+ private refactoringDetails: RefactoringDetailItem[] = [];
+ public targetFile: { original: string; refactored: string } | undefined;
+ public affectedFiles: { original: string; refactored: string }[] = [];
+ public energySaved: number | undefined;
+ public targetSmell: Smell | undefined;
+
+ constructor() {
+ this.resetRefactoringDetails();
+ }
+
+ /**
+ * Updates the view with new refactoring details
+ * @param targetSmell - The code smell being refactored
+ * @param targetFile - Paths to original and refactored target files
+ * @param affectedFiles - Additional files impacted by the refactoring
+ * @param energySaved - Estimated energy savings in kg CO2
+ */
+ updateRefactoringDetails(
+ targetSmell: Smell,
+ targetFile: { original: string; refactored: string },
+ affectedFiles: { original: string; refactored: string }[],
+ energySaved: number | undefined,
+ ): void {
+ this.targetSmell = targetSmell;
+ this.targetFile = targetFile;
+ this.affectedFiles = affectedFiles;
+ this.energySaved = energySaved;
+ this.refactoringDetails = [];
+
+ // Add smell information
+ if (targetSmell) {
+ const smellName =
+ getNameByMessageId(targetSmell.messageId) || targetSmell.messageId;
+ this.refactoringDetails.push(
+ new RefactoringDetailItem(
+ `Refactoring: ${smellName}`,
+ '',
+ '',
+ '',
+ true,
+ false,
+ true,
+ ),
+ );
+ }
+
+ // Add energy savings
+ if (energySaved !== undefined) {
+ this.refactoringDetails.push(
+ new RefactoringDetailItem(
+ `Estimated Savings: ${energySaved} kg CO2`,
+ 'Based on energy impact analysis',
+ '',
+ '',
+ false,
+ true,
+ ),
+ );
+ }
+
+ // Add target file
+ if (targetFile) {
+ this.refactoringDetails.push(
+ new RefactoringDetailItem(
+ `${path.basename(targetFile.original)}`,
+ 'Main refactored file',
+ targetFile.original,
+ targetFile.refactored,
+ affectedFiles.length > 0,
+ ),
+ );
+ }
+
+ this._onDidChangeTreeData.fire(undefined);
+ }
+
+ /**
+ * Resets the view to its initial state
+ */
+ resetRefactoringDetails(): void {
+ this.targetFile = undefined;
+ this.affectedFiles = [];
+ this.targetSmell = undefined;
+ this.energySaved = undefined;
+ this.refactoringDetails = [];
+ this._onDidChangeTreeData.fire(undefined);
+ }
+
+ // VS Code TreeDataProvider implementation
+ getTreeItem(element: RefactoringDetailItem): vscode.TreeItem {
+ return element;
+ }
+
+ getChildren(element?: RefactoringDetailItem): RefactoringDetailItem[] {
+ if (!element) {
+ return this.refactoringDetails;
+ }
+
+ // Handle smell description expansion
+ if (element.isSmellItem && this.targetSmell) {
+ const description =
+ getDescriptionByMessageId(this.targetSmell.messageId) ||
+ this.targetSmell.message;
+ return [
+ new RefactoringDetailItem(
+ '',
+ description,
+ '',
+ '',
+ false,
+ false,
+ false,
+ 'info',
+ ),
+ ];
+ }
+
+ // Handle affected files expansion
+ if (element.isParent && this.affectedFiles.length > 0) {
+ return this.affectedFiles.map(
+ (file) =>
+ new RefactoringDetailItem(
+ path.basename(file.original),
+ 'Affected file',
+ file.original,
+ file.refactored,
+ ),
+ );
+ }
+
+ return [];
+ }
+}
+
+/**
+ * Represents an item in the refactoring details tree view
+ */
+class RefactoringDetailItem extends vscode.TreeItem {
+ constructor(
+ public readonly label: string,
+ public readonly description: string,
+ public readonly originalFilePath: string,
+ public readonly refactoredFilePath: string,
+ public readonly isParent: boolean = false,
+ public readonly isEnergySaved: boolean = false,
+ public readonly isSmellItem: boolean = false,
+ public readonly itemType: 'info' | 'file' | 'none' = 'none',
+ ) {
+ super(
+ label,
+ isParent || isSmellItem
+ ? vscode.TreeItemCollapsibleState.Collapsed
+ : vscode.TreeItemCollapsibleState.None,
+ );
+
+ // Configure item based on type
+ if (isEnergySaved) {
+ this.iconPath = new vscode.ThemeIcon(
+ 'lightbulb',
+ new vscode.ThemeColor('charts.yellow'),
+ );
+ this.tooltip = 'Estimated energy savings from this refactoring';
+ } else if (isSmellItem) {
+ this.iconPath = new vscode.ThemeIcon(
+ 'warning',
+ new vscode.ThemeColor('charts.orange'),
+ );
+ } else if (itemType === 'info') {
+ this.iconPath = new vscode.ThemeIcon('info');
+ this.tooltip = this.description;
+ }
+
+ // Make files clickable to open diffs
+ if (originalFilePath && refactoredFilePath && itemType !== 'info') {
+ this.command = {
+ command: 'ecooptimizer.openDiffEditor',
+ title: 'Compare Changes',
+ arguments: [originalFilePath, refactoredFilePath],
+ };
+ this.contextValue = 'refactoringFile';
+ }
+ }
+}
diff --git a/src/providers/SmellsViewProvider.ts b/src/providers/SmellsViewProvider.ts
new file mode 100644
index 0000000..5694c8a
--- /dev/null
+++ b/src/providers/SmellsViewProvider.ts
@@ -0,0 +1,304 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import * as path from 'path';
+import { buildPythonTree } from '../utils/TreeStructureBuilder';
+import { getAcronymByMessageId } from '../utils/smellsData';
+import { normalizePath } from '../utils/normalizePath';
+import { envConfig } from '../utils/envConfig';
+
+/**
+ * Provides a tree view for displaying code smells in the workspace.
+ * Shows files and their detected smells in a hierarchical structure,
+ * with status indicators and navigation capabilities.
+ */
+export class SmellsViewProvider
+ implements vscode.TreeDataProvider
+{
+ // Event emitter for tree view updates
+ private _onDidChangeTreeData: vscode.EventEmitter<
+ TreeItem | SmellTreeItem | undefined | void
+ > = new vscode.EventEmitter();
+ readonly onDidChangeTreeData: vscode.Event<
+ TreeItem | SmellTreeItem | undefined | void
+ > = this._onDidChangeTreeData.event;
+
+ // Tracks analysis status and smells for each file
+ private fileStatuses: Map = new Map();
+ private fileSmells: Map = new Map();
+
+ constructor(private context: vscode.ExtensionContext) {}
+
+ /**
+ * Triggers a refresh of the tree view
+ */
+ refresh(): void {
+ this._onDidChangeTreeData.fire();
+ }
+
+ /**
+ * Updates the analysis status for a file
+ * @param filePath Path to the file
+ * @param status New status ('queued', 'passed', 'failed', etc.)
+ */
+ setStatus(filePath: string, status: string): void {
+ const normalizedPath = normalizePath(filePath);
+ this.fileStatuses.set(normalizedPath, status);
+
+ // Clear smells if status is outdated
+ if (status === 'outdated') {
+ this.fileSmells.delete(normalizedPath);
+ }
+
+ this._onDidChangeTreeData.fire();
+ }
+
+ /**
+ * Sets the detected smells for a file
+ * @param filePath Path to the file
+ * @param smells Array of detected smells
+ */
+ setSmells(filePath: string, smells: Smell[]): void {
+ this.fileSmells.set(filePath, smells);
+ this._onDidChangeTreeData.fire();
+ }
+
+ /**
+ * Removes a file from the tree view
+ * @param filePath Path to the file to remove
+ * @returns Whether the file was found and removed
+ */
+ public removeFile(filePath: string): boolean {
+ const normalizedPath = normalizePath(filePath);
+ const exists = this.fileStatuses.has(normalizedPath);
+ if (exists) {
+ this.fileStatuses.delete(normalizedPath);
+ this.fileSmells.delete(normalizedPath);
+ }
+ return exists;
+ }
+
+ /**
+ * Clears all file statuses and smells from the view
+ */
+ public clearAllStatuses(): void {
+ this.fileStatuses.clear();
+ this.fileSmells.clear();
+ this._onDidChangeTreeData.fire();
+ }
+
+ getTreeItem(element: TreeItem | SmellTreeItem): vscode.TreeItem {
+ return element;
+ }
+
+ /**
+ * Builds the tree view hierarchy
+ * @param element The parent element or undefined for root items
+ * @returns Promise resolving to child tree items
+ */
+ async getChildren(
+ element?: TreeItem | SmellTreeItem,
+ ): Promise<(TreeItem | SmellTreeItem)[]> {
+ const rootPath = this.context.workspaceState.get(
+ envConfig.WORKSPACE_CONFIGURED_PATH!,
+ );
+ if (!rootPath) {
+ return [];
+ }
+
+ // Smell nodes are leaf nodes - no children
+ if (element instanceof SmellTreeItem) {
+ return [];
+ }
+
+ // If this is a file node, show its smells
+ if (
+ element?.contextValue === 'file' ||
+ element?.contextValue === 'file_with_smells'
+ ) {
+ const smells = this.fileSmells.get(element.fullPath) ?? [];
+ return smells.map((smell) => new SmellTreeItem(smell));
+ }
+
+ // Root element - show either single file or folder contents
+ if (!element) {
+ const stat = fs.statSync(rootPath);
+ if (stat.isFile()) {
+ return [this.createTreeItem(rootPath, true)];
+ } else if (stat.isDirectory()) {
+ return [this.createTreeItem(rootPath, false)]; // Show root folder as top node
+ }
+ }
+
+ // Folder node - build its contents
+ const currentPath = element?.resourceUri?.fsPath;
+ if (!currentPath) return [];
+
+ const childNodes = buildPythonTree(currentPath);
+ return childNodes.map(({ fullPath, isFile }) =>
+ this.createTreeItem(fullPath, isFile),
+ );
+ }
+
+ /**
+ * Creates a tree item for a file or folder
+ * @param filePath Path to the file/folder
+ * @param isFile Whether this is a file (false for folders)
+ * @returns Configured TreeItem instance
+ */
+ private createTreeItem(filePath: string, isFile: boolean): TreeItem {
+ const label = path.basename(filePath);
+ const status =
+ this.fileStatuses.get(normalizePath(filePath)) ?? 'not_yet_detected';
+ const icon = isFile ? getStatusIcon(status) : new vscode.ThemeIcon('folder');
+ const tooltip = isFile ? getStatusMessage(status) : undefined;
+
+ // Determine collapsible state:
+ // - Folders are always collapsible
+ // - Files are collapsible only if they have smells
+ const collapsibleState = isFile
+ ? this.fileSmells.has(filePath) && this.fileSmells.get(filePath)!.length > 0
+ ? vscode.TreeItemCollapsibleState.Collapsed
+ : vscode.TreeItemCollapsibleState.None
+ : vscode.TreeItemCollapsibleState.Collapsed;
+
+ const baseContext = isFile ? 'file' : 'directory';
+ const item = new TreeItem(label, filePath, collapsibleState, baseContext);
+ item.iconPath = icon;
+ item.tooltip = tooltip;
+
+ // Mark files with smells with special context
+ if (
+ isFile &&
+ this.fileSmells.has(filePath) &&
+ this.fileSmells.get(filePath)!.length > 0
+ ) {
+ item.contextValue = 'file_with_smells';
+ }
+
+ // Show outdated status in description
+ if (status === 'outdated') {
+ item.description = 'outdated';
+ }
+
+ return item;
+ }
+}
+
+/**
+ * Tree item representing a file or folder in the smells view
+ */
+export class TreeItem extends vscode.TreeItem {
+ constructor(
+ label: string,
+ public readonly fullPath: string,
+ collapsibleState: vscode.TreeItemCollapsibleState,
+ contextValue: string,
+ ) {
+ super(label, collapsibleState);
+ this.resourceUri = vscode.Uri.file(fullPath);
+ this.contextValue = contextValue;
+
+ // Make files clickable to open them
+ if (contextValue === 'file' || contextValue === 'file_with_smells') {
+ this.command = {
+ title: 'Open File',
+ command: 'vscode.open',
+ arguments: [this.resourceUri],
+ };
+ }
+ }
+}
+
+/**
+ * Tree item representing a detected code smell
+ */
+export class SmellTreeItem extends vscode.TreeItem {
+ constructor(public readonly smell: Smell) {
+ // Format label with acronym and line numbers
+ const acronym = getAcronymByMessageId(smell.messageId) ?? smell.messageId;
+ const lines = smell.occurences
+ ?.map((occ) => occ.line)
+ .filter((line) => line !== undefined)
+ .sort((a, b) => a - b)
+ .join(', ');
+
+ const label = lines ? `${acronym}: Line ${lines}` : acronym;
+ super(label, vscode.TreeItemCollapsibleState.None);
+
+ this.tooltip = smell.message;
+ this.contextValue = 'smell';
+ this.iconPath = new vscode.ThemeIcon('snake');
+
+ // Set up command to jump to the first occurrence
+ const firstLine = smell.occurences?.[0]?.line;
+ if (smell.path && typeof firstLine === 'number') {
+ this.command = {
+ title: 'Jump to Smell',
+ command: 'ecooptimizer.jumpToSmell',
+ arguments: [smell.path, firstLine - 1],
+ };
+ }
+ }
+}
+
+/**
+ * Gets the appropriate icon for a file's analysis status
+ * @param status Analysis status string
+ * @returns ThemeIcon with appropriate icon and color
+ */
+export function getStatusIcon(status: string): vscode.ThemeIcon {
+ switch (status) {
+ case 'queued':
+ return new vscode.ThemeIcon(
+ 'sync~spin',
+ new vscode.ThemeColor('charts.yellow'),
+ );
+ case 'passed':
+ return new vscode.ThemeIcon('pass', new vscode.ThemeColor('charts.green'));
+ case 'no_issues':
+ return new vscode.ThemeIcon('pass', new vscode.ThemeColor('charts.blue'));
+ case 'failed':
+ return new vscode.ThemeIcon('error', new vscode.ThemeColor('charts.red'));
+ case 'outdated':
+ return new vscode.ThemeIcon('warning', new vscode.ThemeColor('charts.orange'));
+ case 'server_down':
+ return new vscode.ThemeIcon(
+ 'server-process',
+ new vscode.ThemeColor('charts.red'),
+ );
+ case 'refactoring':
+ return new vscode.ThemeIcon('robot', new vscode.ThemeColor('charts.purple'));
+ case 'accept-refactoring':
+ return new vscode.ThemeIcon('warning', new vscode.ThemeColor('charts.yellow'));
+ default:
+ return new vscode.ThemeIcon('circle-outline');
+ }
+}
+
+/**
+ * Gets a human-readable message for an analysis status
+ * @param status Analysis status string
+ * @returns Descriptive status message
+ */
+export function getStatusMessage(status: string): string {
+ switch (status) {
+ case 'queued':
+ return 'Analyzing Smells';
+ case 'passed':
+ return 'Smells Successfully Detected';
+ case 'failed':
+ return 'Error Detecting Smells';
+ case 'no_issues':
+ return 'No Smells Found';
+ case 'outdated':
+ return 'File Outdated - Needs Reanalysis';
+ case 'server_down':
+ return 'Server Unavailable';
+ case 'refactoring':
+ return 'Refactoring Currently Ongoing';
+ case 'accept-refactoring':
+ return 'Successfully Refactored - Needs Reanalysis';
+ default:
+ return 'Smells Not Yet Detected';
+ }
+}
diff --git a/src/types.d.ts b/src/types.d.ts
deleted file mode 100644
index a52ddf3..0000000
--- a/src/types.d.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-export interface Occurrence {
- line: number;
- endLine?: number;
- column: number;
- endColumn?: number;
-}
-
-export interface AdditionalInfo {
- // CRC
- repetitions?: number;
- callString?: string;
- // SCL
- concatTarget?: string;
- innerLoopLine?: number;
-}
-
-export interface Smell {
- type: string; // Type of the smell (e.g., "performance", "convention")
- symbol: string; // Symbolic identifier for the smell (e.g., "cached-repeated-calls")
- message: string; // Detailed description of the smell
- messageId: string; // Unique ID for the smell
- confidence: string; // Confidence level (e.g., "HIGH", "MEDIUM")
- path: string; // Optional: absolute file path
- module: string; // Optional: Module name
- obj?: string; // Optional: Object name associated with the smell (if applicable)
- occurences: Occurrence[]; // Optional: List of occurrences for repeated calls
- additionalInfo: AdditionalInfo;
-}
-
-export interface ChangedFile {
- original: string;
- refactored: string;
-}
-
-export interface RefactoredData {
- tempDir: string;
- targetFile: ChangedFile;
- energySaved: number;
- affectedFiles: ChangedFile[];
-}
-
-export interface RefactorOutput {
- refactoredData?: RefactoredData; // Refactored code as a string
- updatedSmells: Smell[]; //
-}
-
-export interface ActiveDiff {
- files: ChangedFile[];
- isOpen: boolean;
- firstOpen: boolean;
-}
-
-export type SmellDetails = {
- symbol: string;
- message: string;
- colour: string; // RGB colour as a string
-};
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index 1d95989..e91d62b 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -1,54 +1,134 @@
import * as vscode from 'vscode';
-import { getEditor } from '../utils/editorUtils';
-import { ContextManager } from '../context/contextManager';
-import { HoverManager } from './hoverManager';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
+import { ConfigManager } from '../context/configManager';
+import { getEnabledSmells } from '../utils/smellsData';
+/**
+ * The `FileHighlighter` class is responsible for managing and applying visual highlights
+ * to code smells in the VS Code editor. It uses cached smell data to determine which
+ * lines to highlight and applies decorations to the editor accordingly.
+ */
export class FileHighlighter {
- private static instance: FileHighlighter;
- private contextManager: ContextManager;
+ private static instance: FileHighlighter | undefined;
private decorations: vscode.TextEditorDecorationType[] = [];
- private constructor(contextManager: ContextManager) {
- this.contextManager = contextManager;
+ private constructor(private smellsCacheManager: SmellsCacheManager) {
+ this.smellsCacheManager.onSmellsUpdated((target) => {
+ if (target === 'all') {
+ this.updateHighlightsForVisibleEditors();
+ } else {
+ this.updateHighlightsForFile(target);
+ }
+ });
}
- public static getInstance(contextManager: ContextManager): FileHighlighter {
+ /**
+ * Retrieves the singleton instance of the `FileHighlighter` class.
+ * If the instance does not exist, it is created.
+ *
+ * @param smellsCacheManager - The manager responsible for caching and providing smell data.
+ * @returns The singleton instance of `FileHighlighter`.
+ */
+ public static getInstance(
+ smellsCacheManager: SmellsCacheManager,
+ ): FileHighlighter {
if (!FileHighlighter.instance) {
- FileHighlighter.instance = new FileHighlighter(contextManager);
+ FileHighlighter.instance = new FileHighlighter(smellsCacheManager);
}
return FileHighlighter.instance;
}
+ /**
+ * Updates highlights for a specific file if it is currently open in a visible editor.
+ *
+ * @param filePath - The file path of the target file to update highlights for.
+ */
+ private updateHighlightsForFile(filePath: string): void {
+ if (!filePath.endsWith('.py')) {
+ return;
+ }
+
+ const editor = vscode.window.visibleTextEditors.find(
+ (e) => e.document.uri.fsPath === filePath,
+ );
+ if (editor) {
+ this.highlightSmells(editor);
+ }
+ }
+
+ /**
+ * Updates highlights for all currently visible editors.
+ */
+ public updateHighlightsForVisibleEditors(): void {
+ vscode.window.visibleTextEditors.forEach((editor) => {
+ if (!editor.document.fileName.endsWith('.py')) {
+ return;
+ }
+ this.highlightSmells(editor);
+ });
+ }
+
+ /**
+ * Resets all active highlights by disposing of all decorations.
+ */
public resetHighlights(): void {
this.decorations.forEach((decoration) => decoration.dispose());
this.decorations = [];
}
- public highlightSmells(editor: vscode.TextEditor, smells: Smell[]): void {
+ /**
+ * Highlights code smells in the given editor based on cached smell data.
+ * Resets existing highlights before applying new ones.
+ *
+ * @param editor - The text editor to apply highlights to.
+ */
+ public highlightSmells(editor: vscode.TextEditor): void {
this.resetHighlights();
- const config = vscode.workspace.getConfiguration('ecooptimizer.detection');
- const smellsConfig = config.get<{
- [key: string]: { enabled: boolean; colour: string };
- }>('smells', {});
- const useSingleColour = config.get('useSingleColour', false);
- const singleHighlightColour = config.get(
+ const smells = this.smellsCacheManager.getCachedSmells(
+ editor.document.uri.fsPath,
+ );
+
+ if (!smells) {
+ return;
+ }
+
+ const smellColours = ConfigManager.get<{
+ [key: string]: string;
+ }>('smellsColours', {});
+
+ const useSingleColour = ConfigManager.get('useSingleColour', false);
+ const singleHighlightColour = ConfigManager.get(
'singleHighlightColour',
'rgba(255, 204, 0, 0.5)',
);
- const highlightStyle = config.get