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 001/215] 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 002/215] 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 003/215] 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 004/215] 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 005/215] 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 006/215] 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 007/215] 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 008/215] 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 009/215] 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 010/215] 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 011/215] 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 012/215] 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 013/215] 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 014/215] 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 015/215] 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 016/215] 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 017/215] 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 018/215] 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 019/215] 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 020/215] 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 021/215] 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 022/215] 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 023/215] 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 024/215] 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 025/215] 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 026/215] 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 027/215] 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 028/215] 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 029/215] 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 030/215] 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 031/215] 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 032/215] 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 033/215] 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 034/215] 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 035/215] 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 036/215] 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 037/215] 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 038/215] 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 039/215] 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 040/215] 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 041/215] 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 042/215] 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 043/215] 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 044/215] 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 045/215] 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 046/215] 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 047/215] 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 048/215] 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 049/215] 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 050/215] 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 051/215] 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 052/215] 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 053/215] 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 054/215] 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 055/215] 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 056/215] 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 057/215] 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 058/215] 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 059/215] 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 060/215] 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 061/215] 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 062/215] 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 063/215] 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 064/215] 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 065/215] 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 066/215] 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 067/215] 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 068/215] 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 069/215] 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 070/215] 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 071/215] 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 072/215] 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 073/215] 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 074/215] 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 075/215] 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 076/215] 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 077/215] 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 078/215] 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 079/215] [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 080/215] 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 0b03f75ddc9435f48e4d8f084782810aac21d194 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri, 21 Mar 2025 00:17:05 -0400
Subject: [PATCH 081/215] initial integration with new changes
---
.env | 3 +
.prettierrc | 1 +
assets/eco-icon.png | Bin 0 -> 518 bytes
data/smells.json | 93 +++++
package.json | 536 ++++++++++++-------------
src/api/backend.ts | 106 ++---
src/commands/configureWorkspace.ts | 179 +++++++++
src/commands/detectSmells.ts | 259 ++++++++----
src/commands/filterSmells.ts | 72 ++++
src/commands/jumpToSmell.ts | 25 ++
src/commands/openFile.ts | 21 +
src/commands/refactorSmell.ts | 105 ++---
src/commands/resetConfiguration.ts | 39 ++
src/commands/showLogs.ts | 312 +++++++-------
src/commands/toggleSmellLinting.ts | 89 ++--
src/commands/wipeWorkCache.ts | 72 ++--
src/context/SmellsCacheManager.ts | 136 +++++++
src/context/contextManager.ts | 40 +-
src/extension.ts | 323 +++++++--------
src/listeners/fileSaveListener.ts | 48 +++
src/managers/SmellsViewStateManager.ts | 135 +++++++
src/managers/SmellsViewUIManager.ts | 185 +++++++++
src/providers/FilterSmellsProvider.ts | 181 +++++++++
src/providers/SmellsViewProvider.ts | 130 ++++++
src/ui/fileHighlighter.ts | 23 +-
src/ui/hoverManager.ts | 29 +-
src/ui/lineSelectionManager.ts | 23 +-
src/ui/refactorView.ts | 198 ---------
src/utils/envConfig.ts | 7 +-
src/utils/handleEditorChange.ts | 126 ------
src/utils/handleSmellSettings.ts | 53 ---
src/utils/hashDocs.ts | 31 --
src/utils/smellDetails.ts | 74 ----
src/utils/smellsData.ts | 97 +++++
34 files changed, 2327 insertions(+), 1424 deletions(-)
create mode 100644 assets/eco-icon.png
create mode 100644 data/smells.json
create mode 100644 src/commands/configureWorkspace.ts
create mode 100644 src/commands/filterSmells.ts
create mode 100644 src/commands/jumpToSmell.ts
create mode 100644 src/commands/openFile.ts
create mode 100644 src/commands/resetConfiguration.ts
create mode 100644 src/context/SmellsCacheManager.ts
create mode 100644 src/listeners/fileSaveListener.ts
create mode 100644 src/managers/SmellsViewStateManager.ts
create mode 100644 src/managers/SmellsViewUIManager.ts
create mode 100644 src/providers/FilterSmellsProvider.ts
create mode 100644 src/providers/SmellsViewProvider.ts
delete mode 100644 src/ui/refactorView.ts
delete mode 100644 src/utils/handleEditorChange.ts
delete mode 100644 src/utils/handleSmellSettings.ts
delete mode 100644 src/utils/hashDocs.ts
delete mode 100644 src/utils/smellDetails.ts
create mode 100644 src/utils/smellsData.ts
diff --git a/.env b/.env
index 86798f4..dfd4ddf 100644
--- a/.env
+++ b/.env
@@ -4,3 +4,6 @@ FILE_CHANGES_KEY='lastSavedHashes'
LAST_USED_SMELLS_KEY='lastUsedSmells'
CURRENT_REFACTOR_DATA_KEY='refactorData'
ACTIVE_DIFF_KEY='activeDiff'
+FILE_HASH_CACHE_KEY='fileHashCache'
+SMELL_CACHE_KEY='smellCache'
+SMELL_LINTING_ENABLED_KEY='smellLintingEnabled'
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/assets/eco-icon.png b/assets/eco-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5314b77ab490c287f3d739c6a4d540bf74020a5
GIT binary patch
literal 518
zcmV+h0{Q)kP)kB1_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}c/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"
+ },
"contributes": {
+ "viewsContainers": {
+ "activitybar": [
+ {
+ "id": "ecooptimizer",
+ "title": "Eco Optimizer",
+ "icon": "assets/eco-icon.png"
+ }
+ ]
+ },
+ "views": {
+ "ecooptimizer": [
+ {
+ "id": "ecooptimizer.view",
+ "name": "Code Smells",
+ "icon": "assets/eco-icon.png"
+ },
+ {
+ "id": "ecooptimizer.filterView",
+ "name": "Filter Smells",
+ "icon": "assets/eco-icon.png"
+ }
+ ]
+ },
+ "viewsWelcome": [
+ {
+ "view": "ecooptimizer.view",
+ "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"
+ }
+ ],
"commands": [
{
- "command": "ecooptimizer.detectSmells",
+ "command": "ecooptimizer.configureWorkspace",
+ "title": "Configure Workspace",
+ "category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.resetConfiguration",
+ "title": "Reset Configuration",
+ "category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.toggleSmellFilter",
+ "title": "Toggle Smell",
+ "category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.editSmellFilterOption",
+ "title": "Edit Option",
+ "icon": "$(edit)",
+ "category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.selectAllFilterSmells",
+ "title": "Select All Smells",
+ "category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.deselectAllFilterSmells",
+ "title": "Deselect All Smells",
+ "category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.openFile",
+ "title": "Open File",
+ "category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.detectSmellsFolder",
+ "title": "Detect Smells for All Files",
+ "icon": "$(search)",
+ "category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.detectSmellsFile",
"title": "Detect Smells",
- "category": "Eco"
+ "icon": "$(search)",
+ "category": "Eco Optimizer"
},
{
"command": "ecooptimizer.refactorSmell",
@@ -33,26 +206,20 @@
"category": "Eco"
},
{
- "command": "ecooptimizer.wipeWorkCache",
- "title": "Wipe Workspace Cache",
- "category": "Eco"
- },
- {
- "command": "ecooptimizer.showRefactorSidebar",
- "title": "Show Refactor Sidebar",
- "category": "Eco"
+ "command": "ecooptimizer.refactorAllSmellsOfType",
+ "title": "Refactor Smells By Type",
+ "icon": "$(tools)",
+ "category": "Eco Optimizer"
},
{
- "command": "ecooptimizer.pauseRefactorSidebar",
- "title": "Pause Refactor Sidebar",
- "category": "Eco",
- "enablement": "false"
+ "command": "ecooptimizer.jumpToSmell",
+ "title": "Jump to Smell in File",
+ "category": "Eco Optimizer"
},
{
- "command": "ecooptimizer.clearRefactorSidebar",
- "title": "Clear Refactor Sidebar",
- "category": "Eco",
- "enablement": "false"
+ "command": "ecooptimizer.wipeWorkCache",
+ "title": "Clear Smells Cache",
+ "category": "Eco Optimizer"
},
{
"command": "ecooptimizer.startLogging",
@@ -67,6 +234,50 @@
}
],
"menus": {
+ "view/title": [
+ {
+ "command": "ecooptimizer.resetConfiguration",
+ "when": "view == ecooptimizer.view && workspaceState.workspaceConfigured",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.wipeWorkCache",
+ "when": "view == ecooptimizer.view && workspaceState.workspaceConfigured",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.selectAllFilterSmells",
+ "when": "view == ecooptimizer.filterView",
+ "group": "resource"
+ },
+ {
+ "command": "ecooptimizer.deselectAllFilterSmells",
+ "when": "view == ecooptimizer.filterView",
+ "group": "resource"
+ }
+ ],
+ "view/item/context": [
+ {
+ "command": "ecooptimizer.editSmellFilterOption",
+ "when": "viewItem == smellOption",
+ "group": "inline"
+ },
+ {
+ "command": "ecooptimizer.detectSmellsFolder",
+ "when": "viewItem == ecoOptimizerFolder",
+ "group": "inline"
+ },
+ {
+ "command": "ecooptimizer.detectSmellsFile",
+ "when": "viewItem == ecoOptimizerFile",
+ "group": "inline"
+ },
+ {
+ "command": "ecooptimizer.refactorAllSmellsOfType",
+ "when": "viewItem == ecoOptimizerFile",
+ "group": "inline"
+ }
+ ],
"editor/title": [
{
"command": "ecooptimizer.toggleSmellLinting",
@@ -101,165 +312,53 @@
"default": "",
"description": "Path to store log files and output reports. Defaults to a 'logs' folder inside the workspace."
},
- "detection.smells": {
+ "detection.smellColours": {
"order": 1,
"type": "object",
"additionalProperties": false,
- "description": "Configure which smells to detect and their highlight colours.",
+ "description": "Configure the highlight colours of each smell (css syntax).",
"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"
},
"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"
},
"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"
},
"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"
},
"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"
},
"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"
},
"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"
},
"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"
}
}
},
@@ -292,131 +391,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..49aefc9 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -19,12 +19,17 @@ export async function checkServerStatus(): Promise {
}
}
+/**
+ * Initializes and synchronizes logs with the backend.
+ *
+ * @param {string} log_dir - The directory where logs are stored.
+ * @returns {Promise} - Returns `true` if the logs are successfully initialized and synchronized, otherwise throws an error.
+ * @throws {Error} - Throws an error if the initialization fails due to network issues or backend errors.
+ */
export async function initLogs(log_dir: string): Promise {
const url = `${BASE_URL}/logs/init`;
try {
- console.log('Initializing and synching logs with backend');
-
const response = await fetch(url, {
method: 'POST',
headers: {
@@ -34,71 +39,73 @@ export async function initLogs(log_dir: string): Promise {
});
if (!response.ok) {
- console.error(`Unable to initialize logging: ${JSON.stringify(response)}`);
-
- return false;
+ throw new Error(`Unable to initialize logging: ${response.statusText}`);
}
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;
+ if (error instanceof Error) {
+ throw new Error(`Eco: Unable to initialize logging: ${error.message}`);
+ } else {
+ throw new Error('Eco: An unexpected error occurred while initializing logs.');
+ }
}
}
-// ✅ Fetch detected smells for a given file (only enabled smells)
+/**
+ * Sends a request to the backend to detect code smells in the specified file.
+ *
+ * @param filePath - The absolute path to the file being analyzed.
+ * @param enabledSmells - A dictionary containing enabled smells and their configured options.
+ * @returns A promise resolving to the backend response or throwing an error if unsuccessful.
+ */
export async function fetchSmells(
filePath: string,
- enabledSmells: string[],
-): Promise {
+ enabledSmells: Record>
+): Promise<{ smells: Smell[]; status: number }> {
const url = `${BASE_URL}/smells`;
try {
- 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
+ method: "POST",
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,
+ }),
});
if (!response.ok) {
- console.error(
- `Eco: API request failed (${response.status} - ${response.statusText})`,
+ throw new Error(
+ `Backend request failed with status ${response.status}: ${response.statusText}`
);
- vscode.window.showErrorMessage(
- `Eco: Failed to fetch smells`,
- );
- return [];
}
- const smellsList = (await response.json()) as Smell[];
+ const smellsList = await response.json();
if (!Array.isArray(smellsList)) {
- console.error('Eco: Invalid response format from backend.');
- vscode.window.showErrorMessage('Eco: Failed to fetch smells');
- return [];
+ throw new Error("Unexpected response format from backend.");
}
- 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',
+ throw new Error(
+ `Failed to connect to the backend: ${error.message}. Please check your network and try again.`
);
- return [];
}
}
-// Request refactoring for a specific smell
+
+/**
+ * Refactors a specific code smell in a given file.
+ *
+ * @param {string} filePath - The path to the file containing the code smell.
+ * @param {Smell} smell - The code smell to refactor.
+ * @returns {Promise} - The result of the refactoring operation.
+ * @throws {Error} - Throws an error if the workspace folder cannot be determined, the API request fails, or an unexpected error occurs.
+ */
export async function refactorSmell(
filePath: string,
smell: Smell,
@@ -107,21 +114,14 @@ export async function refactorSmell(
const workspaceFolder = vscode.workspace.workspaceFolders?.find((folder) =>
filePath.includes(folder.uri.fsPath),
- )
+ );
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}`,
- );
+ throw new Error(`Unable to determine workspace folder for file: ${filePath}`);
}
const workspaceFolderPath = workspaceFolder.uri.fsPath;
- console.log(
- `Eco: Initiating refactoring for smell "${smell.symbol}" in "${workspaceFolderPath}"`,
- );
-
const payload = {
source_dir: workspaceFolderPath,
smell,
@@ -138,16 +138,16 @@ 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}`);
+ throw new Error(`Refactoring failed for smell "${smell.symbol}": ${errorText}`);
}
const refactorResult = (await response.json()) as RefactorOutput;
return refactorResult;
} catch (error) {
- console.error('Eco: Unexpected error in refactorSmell:', error);
- throw error;
+ if (error instanceof Error) {
+ throw new Error(`Unexpected error during refactoring: ${error.message}`);
+ } else {
+ throw new Error('An unexpected error occurred during refactoring.');
+ }
}
-}
+}
\ No newline at end of file
diff --git a/src/commands/configureWorkspace.ts b/src/commands/configureWorkspace.ts
new file mode 100644
index 0000000..7be0c7d
--- /dev/null
+++ b/src/commands/configureWorkspace.ts
@@ -0,0 +1,179 @@
+import * as vscode from 'vscode';
+import * as path from 'path';
+import * as fs from 'fs';
+
+/**
+ * Prompts the user to configure a workspace by selecting either a Python file or folder.
+ * Updates the workspace state accordingly and refreshes the tree view to reflect the changes.
+ *
+ * @param context - The extension context used to persist workspace state.
+ * @param treeDataProvider - The tree data provider responsible for refreshing the workspace view.
+ */
+export async function configureWorkspace(
+ context: vscode.ExtensionContext,
+ treeDataProvider: vscode.TreeDataProvider,
+) {
+ // Prompt the user to choose between configuring a Python file or folder
+ const choice = await vscode.window.showQuickPick(
+ ['Configure a Python File', 'Configure a Python Folder'],
+ { placeHolder: 'Choose whether to configure a Python file or folder.' },
+ );
+
+ // Exit if the user cancels the selection
+ if (!choice) return;
+
+ // Call the appropriate function based on the user's choice
+ if (choice === 'Configure a Python File') {
+ await configurePythonFile(context, treeDataProvider);
+ } else {
+ await configurePythonFolder(context, treeDataProvider);
+ }
+}
+
+/**
+ * Prompts the user to select a Python file from open editors or the workspace.
+ * Updates the workspace state with the selected file and refreshes the tree view.
+ *
+ * @param context - The extension context used to persist workspace state.
+ * @param treeDataProvider - The tree data provider responsible for refreshing the workspace view.
+ */
+async function configurePythonFile(
+ context: vscode.ExtensionContext,
+ treeDataProvider: vscode.TreeDataProvider,
+) {
+ // Retrieve Python files from open editors
+ const openEditorFiles = vscode.window.tabGroups.activeTabGroup.tabs
+ .map((tab) => (tab.input as any)?.uri?.fsPath)
+ .filter((filePath) => filePath && filePath.endsWith('.py'));
+
+ // Retrieve Python files from the workspace using a glob pattern
+ const workspaceFiles = await vscode.workspace.findFiles(
+ '**/*.py',
+ '**/node_modules/**',
+ );
+ const workspaceFilePaths = workspaceFiles.map((uri) => uri.fsPath);
+
+ // Combine and deduplicate the list of Python files
+ const allPythonFiles = Array.from(
+ new Set([...openEditorFiles, ...workspaceFilePaths]),
+ );
+
+ // Notify the user if no Python files are found
+ if (allPythonFiles.length === 0) {
+ vscode.window.showErrorMessage(
+ 'No Python files found in open editors or workspace.',
+ );
+ return;
+ }
+
+ // Prompt the user to select a Python file from the combined list
+ const selectedFile = await vscode.window.showQuickPick(allPythonFiles, {
+ placeHolder: 'Select a Python file to use as your workspace.',
+ });
+
+ // Update the workspace state and notify the user if a file is selected
+ if (selectedFile) {
+ await updateWorkspace(context, treeDataProvider, selectedFile);
+ vscode.window.showInformationMessage(
+ `Workspace configured for file: ${path.basename(selectedFile)}`,
+ );
+ }
+}
+
+/**
+ * Prompts the user to select a Python folder from the workspace.
+ * Updates the workspace state with the selected folder and refreshes the tree view.
+ *
+ * @param context - The extension context used to persist workspace state.
+ * @param treeDataProvider - The tree data provider responsible for refreshing the workspace view.
+ */
+async function configurePythonFolder(
+ context: vscode.ExtensionContext,
+ treeDataProvider: vscode.TreeDataProvider,
+) {
+ // Retrieve the workspace folders from the current workspace
+ const workspaceFolders = vscode.workspace.workspaceFolders;
+
+ // Notify the user if no workspace folders are found
+ if (!workspaceFolders || workspaceFolders.length === 0) {
+ vscode.window.showErrorMessage(
+ 'No workspace folders found. Open a project in Explorer first.',
+ );
+ return;
+ }
+
+ // Filter workspace folders to include only those containing Python files
+ const validPythonFolders = workspaceFolders
+ .map((folder) => folder.uri.fsPath)
+ .filter((folderPath) => containsPythonFiles(folderPath));
+
+ // Notify the user if no valid Python folders are found
+ if (validPythonFolders.length === 0) {
+ vscode.window.showErrorMessage(
+ 'No valid Python folders found in your workspace.',
+ );
+ return;
+ }
+
+ // Prompt the user to select a Python folder from the filtered list
+ const selectedFolder = await vscode.window.showQuickPick(validPythonFolders, {
+ placeHolder: 'Select a Python folder to use as your workspace.',
+ });
+
+ // Update the workspace state and notify the user if a folder is selected
+ if (selectedFolder) {
+ await updateWorkspace(context, treeDataProvider, selectedFolder);
+ vscode.window.showInformationMessage(
+ `Workspace configured for folder: ${path.basename(selectedFolder)}`,
+ );
+ }
+}
+
+/**
+ * Checks if a given folder contains Python files.
+ * This function scans the folder for `.py` files or an `__init__.py` file.
+ *
+ * @param folderPath - The absolute path of the folder to check.
+ * @returns True if the folder contains Python files, otherwise false.
+ */
+function containsPythonFiles(folderPath: string): boolean {
+ try {
+ // Read the contents of the folder
+ const files = fs.readdirSync(folderPath);
+
+ // Check if any file ends with `.py` or if the folder contains `__init__.py`
+ return (
+ files.some((file) => file.endsWith('.py')) || files.includes('__init__.py')
+ );
+ } catch (error) {
+ // Return false if an error occurs (e.g., folder is inaccessible)
+ return false;
+ }
+}
+
+/**
+ * Updates the workspace state to reflect the configured Python file or folder.
+ * Refreshes the tree view to reflect the changes.
+ *
+ * @param context - The extension context used to persist workspace state.
+ * @param treeDataProvider - The tree data provider responsible for refreshing the workspace view.
+ * @param workspacePath - The selected workspace path (file or folder).
+ */
+async function updateWorkspace(
+ context: vscode.ExtensionContext,
+ treeDataProvider: vscode.TreeDataProvider,
+ workspacePath: string,
+) {
+ // Update the workspace state with the selected path
+ await context.workspaceState.update('workspaceConfiguredPath', workspacePath);
+
+ // Set a context variable to indicate that the workspace is configured
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'workspaceState.workspaceConfigured',
+ true,
+ );
+
+ // Refresh the tree view to reflect the updated workspace configuration
+ (treeDataProvider as any).refresh();
+}
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index f597f96..5681670 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -1,121 +1,208 @@
import * as vscode from 'vscode';
-
-import { FileHighlighter } from '../ui/fileHighlighter';
-import { getEditorAndFilePath } from '../utils/editorUtils';
+import * as fs from 'fs';
+import * as path from 'path';
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 { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+import { getEnabledSmells } from '../utils/smellsData';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
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.',
- );
+/**
+ * Detects code smells for a given file.
+ * Uses cached smells if available; otherwise, fetches from the backend.
+ *
+ * @param smellsCacheManager - Manages caching of smells and file hashes.
+ * @param treeDataProvider - UI provider for updating tree view.
+ * @param fileUri - The VS Code file URI or string path of the file to analyze.
+ */
+export async function detectSmellsFile(
+ smellsCacheManager: SmellsCacheManager,
+ treeDataProvider: SmellsDisplayProvider,
+ fileUri: vscode.Uri | string,
+) {
+ // Validate the file URI or path
+ if (!fileUri) {
+ vscode.window.showErrorMessage('No file selected for analysis.');
+ return;
}
-});
-export interface SmellDetectRecord {
- hash: string;
- smells: Smell[];
-}
+ // Convert file URI to a path if necessary
+ const filePath = typeof fileUri === 'string' ? fileUri : fileUri.fsPath;
-let fileHighlighter: FileHighlighter;
+ // Handle outdated files before proceeding
+ console.log('Handling outdated file:', filePath);
+ await handleOutdatedFile(filePath, smellsCacheManager, treeDataProvider);
-export async function detectSmells(contextManager: ContextManager): Promise {
- const { editor, filePath } = getEditorAndFilePath();
+ // Open the file and compute its hash
+ const document = await vscode.workspace.openTextDocument(filePath);
+ const fileContent = document.getText();
- // ✅ 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.');
+ // Store the file hash after analyzing
+ console.log('Storing file hash for:', filePath);
+ await smellsCacheManager.storeFileHash(filePath, fileContent);
+
+ // Retrieve enabled smells from configuration
+ console.log('Retrieving enabled smells...');
+ const enabledSmells = getEnabledSmells();
+
+ // Ensure that at least one smell type is enabled
+ if (Object.keys(enabledSmells).length === 0) {
+ vscode.window.showWarningMessage(
+ 'No enabled smells found. Please configure enabled smells in the settings.',
+ );
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.');
+ // Check if smells are already cached
+ console.log('Checking for cached smells...');
+ const cachedSmells = smellsCacheManager.getCachedSmells(filePath);
+ if (cachedSmells !== undefined) {
+ // Use cached smells if available
+ vscode.window.showInformationMessage(
+ `Using cached smells for ${path.basename(filePath)}.`,
+ );
+
+ if (cachedSmells.length > 0) {
+ console.log('Updating UI with cached smells...');
+ treeDataProvider.updateSmells(filePath, cachedSmells, enabledSmells);
+ } else {
+ treeDataProvider.updateStatus(filePath, 'no_issues');
+ }
+
+ console.log('Analysis complete: Using cached smells.');
return;
}
- console.log(`Eco: Detecting smells in file: ${filePath}`);
-
- const enabledSmells = getEnabledSmells();
- if (!Object.values(enabledSmells).includes(true)) {
+ if (serverStatus.getStatus() === ServerStatusType.DOWN) {
vscode.window.showWarningMessage(
- 'Eco: No smells are enabled! Detection skipped.',
+ 'Action blocked: Server is down and no cached smells exist for this file version.',
);
+ treeDataProvider.updateStatus(filePath, 'server_down');
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);
- }
+ // Update UI to indicate the file is queued for analysis
+ treeDataProvider.updateStatus(filePath, 'queued');
- // 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;
- }
+ try {
+ // Prepare enabled smells for backend request
+ console.log('Preparing enabled smells for backend...');
+ const enabledSmellsForBackend = Object.fromEntries(
+ Object.entries(enabledSmells).map(([key, value]) => [key, value.options]),
+ );
- if (smellsData) {
- allSmells[filePath] = { hash: currentFileHash, smells: smellsData };
- contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, allSmells);
+ // Request smell analysis from the backend
+ console.log('Requesting smell analysis from the backend...');
+ const { smells, status } = await fetchSmells(filePath, enabledSmellsForBackend);
+
+ // Handle response and update UI
+ if (status === 200) {
+ // Cache detected smells, even if no smells are found
+ console.log('Caching detected smells...');
+ await smellsCacheManager.setCachedSmells(filePath, smells);
+
+ // Remove the file from modifiedFiles after re-analysis
+ treeDataProvider.clearOutdatedStatus(filePath);
+
+ console.log('Updating UI with detected smells...');
+ if (smells.length > 0) {
+ treeDataProvider.updateSmells(filePath, smells, enabledSmells);
+ vscode.window.showInformationMessage(
+ `Analysis complete: Detected ${
+ smells.length
+ } code smell(s) in ${path.basename(filePath)}.`,
+ );
+ } else {
+ treeDataProvider.updateStatus(filePath, 'no_issues'); // Update status based on backend result
+ vscode.window.showInformationMessage(
+ `Analysis complete: No code smells found in ${path.basename(filePath)}.`,
+ );
+ }
+
+ console.log('Analysis complete: Detected smells.');
+ } else {
+ throw new Error(`Unexpected status code: ${status}`);
}
- } else {
+ } catch (error: any) {
+ // Handle errors during analysis
+ treeDataProvider.updateStatus(filePath, 'failed');
+ vscode.window.showErrorMessage(`Analysis failed: ${error.message}`);
+ }
+}
+
+/**
+ * Detects code smells for all Python files within a folder.
+ * Uses cached smells where available.
+ *
+ * @param smellsCacheManager - Manages caching of smells and file hashes.
+ * @param treeDataProvider - UI provider for updating tree view.
+ * @param folderPath - The absolute path of the folder to analyze.
+ */
+export async function detectSmellsFolder(
+ smellsCacheManager: SmellsCacheManager,
+ treeDataProvider: SmellsDisplayProvider,
+ folderPath: string,
+) {
+ console.log('Detecting smells for all Python files in:', folderPath);
+ // Notify the user that folder analysis has started
+ vscode.window.showInformationMessage(
+ `Detecting code smells for all Python files in: ${path.basename(folderPath)}`,
+ );
+
+ // Retrieve all Python files in the specified folder
+ const pythonFiles = fs
+ .readdirSync(folderPath)
+ .filter((file) => file.endsWith('.py'))
+ .map((file) => path.join(folderPath, file));
+
+ // Ensure that Python files exist in the folder before analysis
+ if (pythonFiles.length === 0) {
vscode.window.showWarningMessage(
- 'Action blocked: Server is down and no cached smells exist for this file version.',
+ `No Python files found in ${path.basename(folderPath)}.`,
);
return;
}
- if (!smellsData || smellsData.length === 0) {
- vscode.window.showInformationMessage('Eco: No code smells detected.');
+ // Retrieve enabled smells from configuration
+ const enabledSmells = getEnabledSmells();
+
+ // Ensure that at least one smell type is enabled
+ if (Object.keys(enabledSmells).length === 0) {
+ vscode.window.showWarningMessage(
+ 'No enabled smells found. Please configure enabled smells in the settings.',
+ );
return;
}
- console.log(`Eco: Highlighting detected smells in ${filePath}.`);
- if (!fileHighlighter) {
- fileHighlighter = FileHighlighter.getInstance(contextManager);
+ // Analyze each Python file in the folder
+ for (const file of pythonFiles) {
+ console.log('Analyzing:', file);
+ await detectSmellsFile(smellsCacheManager, treeDataProvider, file);
}
- fileHighlighter.highlightSmells(editor, smellsData);
- vscode.window.showInformationMessage(
- `Eco: Highlighted ${smellsData.length} smells.`,
- );
+ // Refresh UI to reflect folder analysis results
+ treeDataProvider.refresh();
+}
- // Set the linting state to enabled
- contextManager.setWorkspaceData(envConfig.SMELL_LINTING_ENABLED_KEY, true);
- vscode.commands.executeCommand('setContext', 'eco.smellLintingEnabled', true);
+/**
+ * Handles outdated files before detecting smells.
+ * Deletes cached smells and updates the UI for outdated files.
+ *
+ * @param filePath - The path of the file to analyze.
+ * @param smellsCacheManager - Manages caching of smells and file hashes.
+ * @param smellsDisplayProvider - The UI provider for updating the tree view.
+ */
+async function handleOutdatedFile(
+ filePath: string,
+ smellsCacheManager: SmellsCacheManager,
+ smellsDisplayProvider: SmellsDisplayProvider,
+) {
+ // Check if the file is marked as outdated
+ if (smellsDisplayProvider.isFileOutdated(filePath)) {
+ // Delete cached smells for the outdated file
+ await smellsCacheManager.clearCachedSmellsForFile(filePath);
+
+ // Remove the outdated status from the UI
+ smellsDisplayProvider.updateStatus(filePath, 'queued'); // Reset to "queued" or another default status
+ }
}
diff --git a/src/commands/filterSmells.ts b/src/commands/filterSmells.ts
new file mode 100644
index 0000000..590ca6e
--- /dev/null
+++ b/src/commands/filterSmells.ts
@@ -0,0 +1,72 @@
+import * as vscode from 'vscode';
+import { FilterSmellsProvider } from '../providers/FilterSmellsProvider';
+
+/**
+ * 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: FilterSmellsProvider,
+) {
+ /**
+ * 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);
+ }),
+ );
+}
diff --git a/src/commands/jumpToSmell.ts b/src/commands/jumpToSmell.ts
new file mode 100644
index 0000000..94fb8ff
--- /dev/null
+++ b/src/commands/jumpToSmell.ts
@@ -0,0 +1,25 @@
+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);
+ editor.revealRange(
+ new vscode.Range(position, position),
+ vscode.TextEditorRevealType.InCenter,
+ );
+ } catch (error: any) {
+ vscode.window.showErrorMessage(
+ `Failed to jump to smell in ${filePath}: ${error.message}`,
+ );
+ }
+}
diff --git a/src/commands/openFile.ts b/src/commands/openFile.ts
new file mode 100644
index 0000000..47687bf
--- /dev/null
+++ b/src/commands/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/refactorSmell.ts b/src/commands/refactorSmell.ts
index 9d9a4d8..ffd323c 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -5,13 +5,11 @@ 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';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
/* istanbul ignore next */
serverStatus.on('change', (newStatus: ServerStatusType) => {
@@ -43,12 +41,13 @@ async function refactorLine(
}
export async function refactorSelectedSmell(
- contextManager: ContextManager,
+ context: vscode.ExtensionContext,
+ smellsCacheManager: SmellsCacheManager,
smellGiven?: Smell,
): Promise {
const { editor, filePath } = getEditorAndFilePath();
- const pastData = contextManager.getWorkspaceData(
+ const pastData = context.workspaceState.get(
envConfig.CURRENT_REFACTOR_DATA_KEY!,
);
@@ -67,9 +66,7 @@ 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!,
- )[filePath].smells;
+ const smellsData = smellsCacheManager.getCachedSmells(filePath);
if (!smellsData || smellsData.length === 0) {
vscode.window.showErrorMessage(
@@ -126,10 +123,10 @@ export async function refactorSelectedSmell(
const { refactoredData } = refactorResult;
- await startRefactoringSession(contextManager, editor, refactoredData);
+ await startRefactoringSession(context, editor, refactoredData);
if (refactorResult.updatedSmells.length) {
- const fileHighlighter = FileHighlighter.getInstance(contextManager);
+ const fileHighlighter = FileHighlighter.getInstance(context, smellsCacheManager);
fileHighlighter.highlightSmells(editor, refactorResult.updatedSmells);
} else {
vscode.window.showWarningMessage(
@@ -140,7 +137,9 @@ export async function refactorSelectedSmell(
export async function refactorAllSmellsOfType(
// eslint-disable-next-line unused-imports/no-unused-vars
- contextManager: ContextManager,
+ context: vscode.ExtensionContext,
+ // eslint-disable-next-line unused-imports/no-unused-vars
+ smellsCacheManager: SmellsCacheManager,
// eslint-disable-next-line unused-imports/no-unused-vars
smellId: string,
): Promise {
@@ -257,55 +256,59 @@ export async function refactorAllSmellsOfType(
/* istanbul ignore next */
async function startRefactoringSession(
- contextManager: ContextManager,
+ context: vscode.ExtensionContext,
editor: vscode.TextEditor,
refactoredData: RefactoredData | MultiRefactoredData,
): Promise {
// Store only the diff editor state
- await contextManager.setWorkspaceData(
+ await context.workspaceState.update(
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.window.showInformationMessage(
+ 'Hey Niv, this needs to be connected to the new refactor sidebar :)',
);
- vscode.commands.executeCommand('ecooptimizer.showRefactorSidebar');
- sidebarState.isOpening = false;
+
+ // 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 {
diff --git a/src/commands/resetConfiguration.ts b/src/commands/resetConfiguration.ts
new file mode 100644
index 0000000..fdd2305
--- /dev/null
+++ b/src/commands/resetConfiguration.ts
@@ -0,0 +1,39 @@
+import * as vscode from 'vscode';
+import { SmellsCacheManager } from '../context/SmellsCacheManager'; // Updated import
+import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+
+/**
+ * Resets the workspace configuration by clearing the stored path and wiping cached smells.
+ * Prompts the user for confirmation before performing the reset.
+ *
+ * @param context - The VS Code extension context.
+ * @param smellsCacheManager - The cache manager handling cached smells.
+ * @param treeDataProvider - The tree data provider to refresh the UI.
+ */
+export async function resetConfiguration(
+ context: vscode.ExtensionContext,
+ smellsCacheManager: SmellsCacheManager,
+ treeDataProvider: SmellsDisplayProvider,
+) {
+ const confirm = await vscode.window.showWarningMessage(
+ 'Are you sure you want to reset the workspace configuration? This will remove the currently selected folder and wipe cached smells.',
+ { modal: true },
+ 'Reset',
+ );
+
+ if (confirm === 'Reset') {
+ await context.workspaceState.update('workspaceConfiguredPath', undefined);
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'workspaceState.workspaceConfigured',
+ false,
+ );
+
+ // Clear smells & refresh UI using SmellsCacheManager
+ await smellsCacheManager.clearCacheAndRefreshUI(treeDataProvider);
+
+ vscode.window.showInformationMessage(
+ 'Workspace configuration has been reset. All cached smells have been cleared.',
+ );
+ }
+}
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
index 38229d3..93b5481 100644
--- a/src/commands/showLogs.ts
+++ b/src/commands/showLogs.ts
@@ -4,7 +4,8 @@ import WebSocket from 'ws';
import { initLogs } from '../api/backend';
import { envConfig } from '../utils/envConfig';
import { serverStatus, ServerStatusType } from '../utils/serverStatus';
-import { globalData } from '../extension';
+
+const WEBSOCKET_BASE_URL = `ws://${envConfig.SERVER_URL}/logs`;
class LogInitializationError extends Error {
constructor(message: string) {
@@ -13,176 +14,177 @@ 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 contextManager 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();
+
+ console.log(logEvent);
+ console.log('Level:', level);
+
+ 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
index d4b4f54..c605645 100644
--- a/src/commands/toggleSmellLinting.ts
+++ b/src/commands/toggleSmellLinting.ts
@@ -1,50 +1,49 @@
-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';
+// import * as vscode from 'vscode';
+// import { ContextManager } from '../context/contextManager';
+// 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;
+// 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);
+// // Update state immediately for UI responsiveness
+// vscode.commands.executeCommand('setContext', 'eco.smellLintingEnabled', newState);
- // Use the singleton instance of FileHighlighter
- const fileHighlighter = FileHighlighter.getInstance(contextManager);
+// // 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
+// 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,
- );
- }
-}
+// 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/wipeWorkCache.ts b/src/commands/wipeWorkCache.ts
index 345a723..768c780 100644
--- a/src/commands/wipeWorkCache.ts
+++ b/src/commands/wipeWorkCache.ts
@@ -1,52 +1,28 @@
import * as vscode from 'vscode';
-import { ContextManager } from '../context/contextManager';
-import { envConfig } from '../utils/envConfig';
-import { updateHash } from '../utils/hashDocs';
-
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
+import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+
+/**
+ * Clears the smells cache and refreshes the UI.
+ * @param smellsCacheManager - Manages the caching of smells and file hashes.
+ * @param smellsDisplayProvider - The UI provider for updating the tree view.
+ */
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.`,
- );
+ smellsCacheManager: SmellsCacheManager,
+ smellsDisplayProvider: SmellsDisplayProvider,
+) {
+ const userResponse = await vscode.window.showWarningMessage(
+ 'Are you sure you want to clear the smells cache? This action cannot be undone.',
+ { modal: true },
+ 'Confirm',
+ );
+
+ if (userResponse === 'Confirm') {
+ // Use SmellsCacheManager to clear cache & refresh UI
+ await smellsCacheManager.clearCacheAndRefreshUI(smellsDisplayProvider);
+
+ vscode.window.showInformationMessage('Smells cache cleared successfully.');
+ } else {
+ vscode.window.showInformationMessage('Operation cancelled.');
}
}
diff --git a/src/context/SmellsCacheManager.ts b/src/context/SmellsCacheManager.ts
new file mode 100644
index 0000000..b015ea9
--- /dev/null
+++ b/src/context/SmellsCacheManager.ts
@@ -0,0 +1,136 @@
+import * as vscode from 'vscode';
+import { createHash } from 'crypto';
+import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+import { envConfig } from '../utils/envConfig';
+
+/**
+ * Manages caching of detected smells and file hashes to avoid redundant backend calls.
+ */
+export class SmellsCacheManager {
+ constructor(private context: vscode.ExtensionContext) {}
+
+ // ============================
+ // Smell Caching Methods
+ // ============================
+
+ /**
+ * Retrieves cached smells for a given file.
+ * If the file has been analyzed and found clean, this will return an empty array.
+ *
+ * @param filePath - The absolute path of the file.
+ * @returns An array of detected smells or `undefined` if the file has not been analyzed.
+ */
+ public getCachedSmells(filePath: string): Smell[] | undefined {
+ const cache = this.context.workspaceState.get>(
+ envConfig.SMELL_CACHE_KEY!,
+ {},
+ );
+
+ return cache[filePath]; // Returns an array of smells or `undefined` if not cached
+ }
+
+ /**
+ * Caches detected smells for a given file.
+ * If no smells are detected, caches an empty array to avoid redundant backend calls.
+ *
+ * @param filePath - The absolute path of the file.
+ * @param smells - The detected smells to store (empty array if no smells found).
+ */
+ public async setCachedSmells(filePath: string, smells: Smell[]): Promise {
+ const cache = this.context.workspaceState.get>(
+ envConfig.SMELL_CACHE_KEY!,
+ {},
+ );
+
+ cache[filePath] = smells; // Store detected smells or an empty array if clean
+
+ await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
+ }
+
+ /**
+ * Clears all cached smells from the workspace.
+ * This forces a fresh analysis of all files when `detectSmellsFile` is called.
+ */
+ public async clearSmellsCache(): Promise {
+ await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, undefined);
+ }
+
+ /**
+ * Clears cached smells for a specific file.
+ *
+ * @param filePath - The path of the file to clear.
+ */
+ public async clearCachedSmellsForFile(filePath: string): Promise {
+ const cache = this.context.workspaceState.get>(
+ envConfig.SMELL_CACHE_KEY!,
+ {},
+ );
+
+ delete cache[filePath]; // Remove the file's cached smells
+
+ await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
+ }
+
+ // ============================
+ // File Hash Caching Methods
+ // ============================
+
+ /**
+ * Computes a SHA256 hash of a file's contents.
+ * @param content - The file content as a string.
+ * @returns A SHA256 hash string.
+ */
+ public computeFileHash(content: string): string {
+ return createHash('sha256').update(content).digest('hex');
+ }
+
+ /**
+ * Stores a hash of a file's contents in workspaceState.
+ * @param filePath - Absolute path of the file.
+ * @param hash - The computed file hash.
+ */
+ public async storeFileHash(filePath: string, hash: string): Promise {
+ const hashes = this.context.workspaceState.get>(
+ envConfig.FILE_HASH_CACHE_KEY!,
+ {},
+ );
+ hashes[filePath] = hash;
+ await this.context.workspaceState.update(envConfig.FILE_HASH_CACHE_KEY!, hashes);
+ }
+
+ /**
+ * Retrieves the stored hash for a given file.
+ * @param filePath - Absolute path of the file.
+ * @returns The stored hash or undefined if not found.
+ */
+ public getStoredFileHash(filePath: string): string | undefined {
+ const hashes = this.context.workspaceState.get>(
+ envConfig.FILE_HASH_CACHE_KEY!,
+ {},
+ );
+ return hashes[filePath];
+ }
+
+ // ============================
+ // UI Refresh Methods
+ // ============================
+
+ /**
+ * Clears all cached smells and refreshes the UI.
+ * Used by both "Clear Smells Cache" and "Reset Configuration".
+ *
+ * @param smellsDisplayProvider - The tree view provider responsible for the UI.
+ */
+ public async clearCacheAndRefreshUI(
+ smellsDisplayProvider: SmellsDisplayProvider,
+ ): Promise {
+ // Remove all cached smells from the workspace state
+ await this.clearSmellsCache();
+
+ // Reset the UI state, including icons and dropdowns
+ smellsDisplayProvider.resetAllSmells();
+
+ // Refresh the UI to reflect the cleared cache
+ smellsDisplayProvider.refresh();
+ }
+}
diff --git a/src/context/contextManager.ts b/src/context/contextManager.ts
index 896255c..067b008 100644
--- a/src/context/contextManager.ts
+++ b/src/context/contextManager.ts
@@ -1,5 +1,9 @@
import * as vscode from 'vscode';
+/**
+ * Manages persistent data storage within VS Code's workspace state.
+ * This includes global and workspace-specific data.
+ */
export class ContextManager {
public context: vscode.ExtensionContext;
@@ -7,7 +11,17 @@ export class ContextManager {
this.context = context;
}
- // Global state example
+ // ============================
+ // Global State (Persists across VS Code sessions)
+ // ============================
+
+ /**
+ * Retrieves globally stored data that persists across VS Code sessions.
+ *
+ * @param key - The key associated with the stored value.
+ * @param defaultVal - The default value to return if the key does not exist.
+ * @returns The stored data or the default value.
+ */
public getGlobalData(
key: string,
defaultVal: any = undefined,
@@ -15,11 +29,27 @@ export class ContextManager {
return this.context.globalState.get(key, defaultVal);
}
+ /**
+ * Updates global data that persists across VS Code sessions.
+ *
+ * @param key - The key for storing the value.
+ * @param value - The value to store.
+ */
public setGlobalData(key: string, value: any): Thenable {
return this.context.globalState.update(key, value);
}
- // Workspace state example
+ // ============================
+ // Workspace State (Resets per workspace)
+ // ============================
+
+ /**
+ * Retrieves workspace-specific data that resets when the user changes workspaces.
+ *
+ * @param key - The key associated with the stored value.
+ * @param defaultVal - The default value to return if the key does not exist.
+ * @returns The stored data or the default value.
+ */
public getWorkspaceData(
key: string,
defaultVal: any = undefined,
@@ -27,6 +57,12 @@ export class ContextManager {
return this.context.workspaceState.get(key, defaultVal);
}
+ /**
+ * Updates workspace-specific data.
+ *
+ * @param key - The key for storing the value.
+ * @param value - The value to store.
+ */
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 10fc13a..940c227 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,70 +1,107 @@
+// eslint-disable-next-line unused-imports/no-unused-imports
import { envConfig } from './utils/envConfig';
+
import * as vscode from 'vscode';
-import { detectSmells } from './commands/detectSmells';
+import { configureWorkspace } from './commands/configureWorkspace';
+import { resetConfiguration } from './commands/resetConfiguration';
+import { detectSmellsFile, detectSmellsFolder } from './commands/detectSmells';
+import { openFile } from './commands/openFile';
+import { registerFilterSmellCommands } from './commands/filterSmells';
+import { jumpToSmell } from './commands/jumpToSmell';
+import { wipeWorkCache } from './commands/wipeWorkCache';
+import { SmellsDisplayProvider } from './providers/SmellsViewProvider';
+import { checkServerStatus } from './api/backend';
+import { FilterSmellsProvider } from './providers/FilterSmellsProvider';
+import { SmellsCacheManager } from './context/SmellsCacheManager';
+import { registerFileSaveListener } from './listeners/fileSaveListener';
+import { serverStatus } from './utils/serverStatus';
import {
- refactorSelectedSmell,
refactorAllSmellsOfType,
+ refactorSelectedSmell,
} from './commands/refactorSmell';
-import { wipeWorkCache } from './commands/wipeWorkCache';
-import { 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 { LogManager } from './commands/showLogs';
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,
-};
+let logManager: LogManager;
+/**
+ * Activates the Eco-Optimizer extension and registers all necessary commands, providers, and listeners.
+ * @param context - The VS Code extension context.
+ */
export function activate(context: vscode.ExtensionContext): void {
- console.log('Eco: Refactor Plugin Activated Successfully');
- const contextManager = new ContextManager(context);
+ console.log('Activating Eco-Optimizer extension...');
- globalData.contextManager = contextManager;
+ // Iniiialize the log manager
+ logManager = new LogManager(context);
- // Show the settings popup if needed
- // TODO: Setting to re-enable popup if disabled
- const settingsPopupChoice =
- contextManager.getGlobalData('showSettingsPopup');
+ // Initialize the SmellsCacheManager for managing caching of smells and file hashes.
+ const smellsCacheManager = new SmellsCacheManager(context);
- if (settingsPopupChoice === undefined || settingsPopupChoice) {
- showSettingsPopup();
- }
-
- console.log('environment variables:', envConfig);
+ // Initialize the Code Smells View.
+ const smellsDisplayProvider = new SmellsDisplayProvider(context);
+ const codeSmellsView = vscode.window.createTreeView('ecooptimizer.view', {
+ treeDataProvider: smellsDisplayProvider,
+ });
+ context.subscriptions.push(codeSmellsView);
+ // Start periodic backend status checks (every 10 seconds).
checkServerStatus();
+ setInterval(checkServerStatus, 10000);
- let smellsData = contextManager.getWorkspaceData(envConfig.SMELL_MAP_KEY!) || {};
- contextManager.setWorkspaceData(envConfig.SMELL_MAP_KEY!, smellsData);
+ // Track the workspace configuration state.
+ const workspaceConfigured = Boolean(
+ context.workspaceState.get('workspaceConfiguredPath'),
+ );
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'workspaceState.workspaceConfigured',
+ workspaceConfigured,
+ );
- let fileHashes =
- contextManager.getWorkspaceData(envConfig.FILE_CHANGES_KEY!) || {};
- contextManager.setWorkspaceData(envConfig.FILE_CHANGES_KEY!, fileHashes);
+ // Register workspace-related commands.
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.configureWorkspace', () =>
+ configureWorkspace(context, smellsDisplayProvider),
+ ),
+ );
- // Check server health every 10 seconds
- setInterval(checkServerStatus, 10000);
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.resetConfiguration', () =>
+ resetConfiguration(context, smellsCacheManager, smellsDisplayProvider),
+ ),
+ );
+
+ // Initialize the Filter Smells View.
+ const filterSmellsProvider = new FilterSmellsProvider(context);
+ const filterSmellsView = vscode.window.createTreeView('ecooptimizer.filterView', {
+ treeDataProvider: filterSmellsProvider,
+ showCollapseAll: true,
+ });
+
+ // Associate the TreeView instance with the provider.
+ filterSmellsProvider.setTreeView(filterSmellsView);
- // ===============================================================
- // REGISTER COMMANDS
- // ===============================================================
+ // Register filter-related commands.
+ registerFilterSmellCommands(context, filterSmellsProvider);
- // Detect Smells Command
+ // Register code smell analysis commands.
context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.detectSmells', async () => {
- console.log('Eco: Detect Smells Command Triggered');
- detectSmells(contextManager);
- }),
+ vscode.commands.registerCommand('ecooptimizer.openFile', openFile),
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'ecooptimizer.detectSmellsFolder',
+ (folderPath) =>
+ detectSmellsFolder(smellsCacheManager, smellsDisplayProvider, folderPath),
+ ),
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.detectSmellsFile', (fileUri) =>
+ detectSmellsFile(smellsCacheManager, smellsDisplayProvider, fileUri),
+ ),
);
// Refactor Selected Smell Command
@@ -72,7 +109,7 @@ export function activate(context: vscode.ExtensionContext): void {
vscode.commands.registerCommand('ecooptimizer.refactorSmell', () => {
if (serverStatus.getStatus() === 'up') {
console.log('Eco: Refactor Selected Smell Command Triggered');
- refactorSelectedSmell(contextManager);
+ refactorSelectedSmell(context, smellsCacheManager);
} else {
vscode.window.showWarningMessage('Action blocked: Server is down.');
}
@@ -88,7 +125,7 @@ export function activate(context: vscode.ExtensionContext): void {
console.log(
`Eco: Refactor All Smells of Type Command Triggered for ${smellId}`,
);
- refactorAllSmellsOfType(contextManager, smellId);
+ refactorAllSmellsOfType(context, smellsCacheManager, smellId);
} else {
vscode.window.showWarningMessage('Action blocked: Server is down.');
}
@@ -96,72 +133,29 @@ export function activate(context: vscode.ExtensionContext): void {
),
);
- // 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');
- }),
- );
-
- // screen button go brr
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.toggleSmellLinting', () => {
- console.log('Eco: Toggle Smell Linting Command Triggered');
- toggleSmellLinting(contextManager);
- }),
- );
-
- // ===============================================================
- // REGISTER VIEWS
- // ===============================================================
-
- const refactorProvider = new RefactorSidebarProvider(context);
- context.subscriptions.push(
- vscode.window.registerWebviewViewProvider(
- RefactorSidebarProvider.viewType,
- refactorProvider,
- ),
- );
-
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.showRefactorSidebar', () =>
- refactorProvider.updateView(),
- ),
- );
+ // Register the "Toggle Smell Auto Lint" command.
+ // TODO: Uncomment this block after implementing smell linting
+ // context.subscriptions.push(
+ // vscode.commands.registerCommand('ecooptimizer.toggleSmellLinting', () => {
+ // console.log('Eco: Toggle Smell Linting Command Triggered');
+ // toggleSmellLinting(contextManager);
+ // }),
+ // );
+ // Register the "Jump to Smell" command.
context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.pauseRefactorSidebar', () =>
- refactorProvider.pauseView(),
- ),
+ vscode.commands.registerCommand('ecooptimizer.jumpToSmell', jumpToSmell),
);
+ // Register the "Clear Smells Cache" command.
context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.clearRefactorSidebar', () =>
- refactorProvider.clearView(),
- ),
- );
-
- // ===============================================================
- // ADD LISTENERS
- // ===============================================================
-
- // Register a listener for configuration changes
- context.subscriptions.push(
- vscode.workspace.onDidChangeConfiguration((event) => {
- handleConfigurationChange(event);
+ vscode.commands.registerCommand('ecooptimizer.wipeWorkCache', async () => {
+ await wipeWorkCache(smellsCacheManager, smellsDisplayProvider);
}),
);
- vscode.window.onDidChangeVisibleTextEditors(async (editors) => {
- handleEditorChanges(contextManager, editors);
- });
-
// Adds comments to lines describing the smell
- const lineSelectManager = new LineSelectionManager(contextManager);
+ const lineSelectManager = new LineSelectionManager(smellsCacheManager);
context.subscriptions.push(
vscode.window.onDidChangeTextEditorSelection((event) => {
console.log('Eco: Detected line selection event');
@@ -169,85 +163,54 @@ export function activate(context: vscode.ExtensionContext): void {
}),
);
- // 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);
- }),
- );
-
- // 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 is active
- context.subscriptions.push(
- vscode.workspace.onDidOpenTextDocument(async (document) => {
- console.log('Eco: Detected document opened event');
- await updateHash(contextManager, document);
- }),
- );
-
- // Listen for file save events
+ // Register a listener for configuration changes
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);
- }
+ vscode.workspace.onDidChangeConfiguration((event) => {
+ handleConfigurationChange(event);
}),
);
// 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);
- }
- }
- }
- }),
+ // TODO: Uncomment this block after implementing smell linting
+ // 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 = context.workspaceState.get(
+ // envConfig.SMELL_LINTING_ENABLED_KEY,
+ // false,
+ // );
+ // if (isEnabled) {
+ // console.log('Eco: Smell linting is enabled. Detecting smells...');
+ // await detectSmells(contextManager);
+ // }
+ // }
+ // }
+ // }),
+ // );
+
+ // Register the file save listener to detect outdated files.
+ const fileSaveListener = registerFileSaveListener(
+ smellsCacheManager,
+ smellsDisplayProvider,
);
+ context.subscriptions.push(fileSaveListener);
- // ===============================================================
- // 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();
- }
- });
+ // TODO: Setting to re-enable popup if disabled
+ const settingsPopupChoice = context.globalState.get('showSettingsPopup');
+
+ if (settingsPopupChoice === undefined || settingsPopupChoice) {
+ showSettingsPopup(context);
+ }
}
-function showSettingsPopup(): void {
+function showSettingsPopup(context: vscode.ExtensionContext): void {
// Check if the required settings are already configured
const config = vscode.workspace.getConfiguration('ecooptimizer');
const workspacePath = config.get('projectWorkspacePath', '');
@@ -277,7 +240,7 @@ function showSettingsPopup(): void {
'You can configure the paths later in the settings.',
);
} else if (selection === 'Never show this again') {
- globalData.contextManager!.setGlobalData('showSettingsPopup', false);
+ context.globalState.update('showSettingsPopup', false);
vscode.window.showInformationMessage(
'You can re-enable this popup again in the settings.',
);
@@ -290,7 +253,6 @@ 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
@@ -300,7 +262,10 @@ function handleConfigurationChange(event: vscode.ConfigurationChangeEvent): void
}
}
+/**
+ * Deactivates the Eco-Optimizer extension.
+ */
export function deactivate(): void {
- console.log('Eco: Deactivating Plugin - Stopping Log Watching');
- stopWatchingLogs();
+ console.log('Deactivating Eco-Optimizer extension...');
+ logManager.stopWatchingLogs();
}
diff --git a/src/listeners/fileSaveListener.ts b/src/listeners/fileSaveListener.ts
new file mode 100644
index 0000000..ae07102
--- /dev/null
+++ b/src/listeners/fileSaveListener.ts
@@ -0,0 +1,48 @@
+import * as vscode from 'vscode';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
+import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+import path from 'path';
+
+/**
+ * Listens for file save events to detect outdated files.
+ * @param smellsCacheManager - Manages the caching of smells and file hashes.
+ * @param smellsDisplayProvider - The UI provider for updating the tree view.
+ */
+export function registerFileSaveListener(
+ smellsCacheManager: SmellsCacheManager,
+ smellsDisplayProvider: SmellsDisplayProvider,
+): vscode.Disposable {
+ return vscode.workspace.onDidSaveTextDocument(async (document) => {
+ const filePath = document.fileName;
+
+ // Ignore files that have no cached smells
+ const cachedSmells = smellsCacheManager.getCachedSmells(filePath);
+ if (!cachedSmells) return;
+
+ // Compute the new hash and compare it with the stored hash
+ const newHash = smellsCacheManager.computeFileHash(document.getText());
+ const oldHash = smellsCacheManager.getStoredFileHash(filePath);
+
+ if (oldHash && newHash !== oldHash) {
+ vscode.window.showWarningMessage(
+ `The file "${path.basename(
+ filePath,
+ )}" has been modified since the last analysis.`,
+ );
+
+ // Check if smell linting is enabled
+ // TODO: Uncomment this block after implementing smell linting
+ // const isEnabled = contextManager.getWorkspaceData(
+ // envConfig.SMELL_LINTING_ENABLED_KEY,
+ // false,
+ // );
+ // if (isEnabled) {
+ // console.log('Eco: Smell linting is enabled. Detecting smells...');
+ // await detectSmells(contextManager);
+ // }
+
+ // Mark file as outdated in the UI
+ smellsDisplayProvider.markFileAsOutdated(filePath);
+ }
+ });
+}
diff --git a/src/managers/SmellsViewStateManager.ts b/src/managers/SmellsViewStateManager.ts
new file mode 100644
index 0000000..1e56c31
--- /dev/null
+++ b/src/managers/SmellsViewStateManager.ts
@@ -0,0 +1,135 @@
+import * as path from 'path';
+
+interface DetectedSmell {
+ messageId: string;
+ symbol: string;
+ occurences: { line: number; endLine?: number }[];
+}
+
+interface ProcessedSmell {
+ acronym: string;
+ occurrences: { line: number; endLine?: number }[];
+}
+
+export class SmellsStateManager {
+ private fileStatusMap: Map = new Map();
+ private detectedSmells: Map = new Map();
+ private smellToFileMap: Map = new Map();
+ private modifiedFiles: Map = new Map();
+
+ /**
+ * Updates the detected smells for a file.
+ * @param filePath - The analyzed file path.
+ * @param smells - The detected smells in the file.
+ * @param smellMetadata - Metadata containing message ID and acronym for each smell.
+ */
+ updateSmells(
+ filePath: string,
+ smells: DetectedSmell[],
+ smellMetadata: Record,
+ ): void {
+ this.fileStatusMap.set(filePath, 'passed');
+
+ console.log('Smells:', smells, typeof smells);
+ const formattedSmells: ProcessedSmell[] = smells.map((smell) => {
+ const foundEntry = Object.values(smellMetadata).find(
+ (smellData) => smellData.message_id === smell.messageId,
+ ) as { message_id: string; acronym: string };
+
+ return {
+ acronym: foundEntry ? foundEntry.acronym : smell.messageId,
+ occurrences: smell.occurences.map((occ) => ({
+ line: occ.line,
+ endLine: occ.endLine,
+ })),
+ };
+ });
+
+ this.detectedSmells.set(filePath, formattedSmells);
+
+ const folderPath = path.dirname(filePath);
+ if (!this.detectedSmells.has(folderPath)) {
+ this.detectedSmells.set(folderPath, []);
+ }
+ this.detectedSmells.get(folderPath)?.push(...formattedSmells);
+ }
+
+ /**
+ * Marks a file as outdated.
+ * @param filePath - The path of the modified file.
+ */
+ markFileAsOutdated(filePath: string): void {
+ this.modifiedFiles.set(filePath, true);
+ }
+
+ /**
+ * Clears the outdated status for a file.
+ * @param filePath - The path of the file to clear.
+ */
+ clearOutdatedStatus(filePath: string): void {
+ this.modifiedFiles.delete(filePath);
+ }
+
+ /**
+ * Updates the status of a specific file or folder.
+ * @param filePath - The file or folder path.
+ * @param status - The new status to set.
+ */
+ updateFileStatus(filePath: string, status: string): void {
+ this.fileStatusMap.set(filePath, status);
+ }
+
+ /**
+ * Checks if a file is marked as outdated.
+ * @param filePath - The path of the file to check.
+ * @returns `true` if the file is outdated, `false` otherwise.
+ */
+ isFileOutdated(filePath: string): boolean {
+ return this.modifiedFiles.has(filePath);
+ }
+
+ /**
+ * Clears all detected smells and resets file statuses.
+ */
+ resetAllSmells(): void {
+ this.detectedSmells.clear();
+ this.fileStatusMap.clear();
+ this.modifiedFiles.clear();
+ }
+
+ /**
+ * Retrieves the status of a file.
+ * @param filePath - The path of the file.
+ * @returns The status of the file.
+ */
+ getFileStatus(filePath: string): string {
+ return this.fileStatusMap.get(filePath) || 'not_detected';
+ }
+
+ /**
+ * Retrieves the detected smells for a file.
+ * @param filePath - The path of the file.
+ * @returns An array of smell entries.
+ */
+ getSmellsForFile(filePath: string): ProcessedSmell[] {
+ return this.detectedSmells.get(filePath) || [];
+ }
+
+ /**
+ * Maps a smell description to a file path.
+ * @param smellDescription - The description of the smell.
+ * @param filePath - The path of the file.
+ */
+ mapSmellToFile(smellDescription: string, filePath: string): void {
+ this.smellToFileMap.set(smellDescription, filePath);
+ }
+
+ /**
+ * Retrieves the file path for a smell description.
+ * @param smellDescription - The description of the smell.
+ * @returns The file path, or `undefined` if not found.
+ */
+ getFileForSmell(smellDescription: string): string | undefined {
+ return this.smellToFileMap.get(smellDescription);
+ }
+}
diff --git a/src/managers/SmellsViewUIManager.ts b/src/managers/SmellsViewUIManager.ts
new file mode 100644
index 0000000..d164dca
--- /dev/null
+++ b/src/managers/SmellsViewUIManager.ts
@@ -0,0 +1,185 @@
+import * as vscode from 'vscode';
+import * as path from 'path';
+import * as fs from 'fs';
+import { SmellsStateManager } from './SmellsViewStateManager';
+
+export class SmellsUIManager {
+ constructor(private stateManager: SmellsStateManager) {}
+
+ /**
+ * Creates a tree item for a given element (folder, file, or smell).
+ * @param element - The file or folder path, or a detected smell.
+ */
+ createTreeItem(element: string): vscode.TreeItem {
+ const status = this.stateManager.getFileStatus(element);
+ const hasSmells = this.stateManager.getSmellsForFile(element).length > 0;
+ const isDirectory = fs.existsSync(element) && fs.statSync(element).isDirectory();
+ const isSmellItem = !fs.existsSync(element) && !isDirectory;
+
+ // Check if the file is outdated
+ const isOutdated =
+ !isDirectory && !isSmellItem && this.stateManager.isFileOutdated(element);
+
+ // Set the collapsible state
+ let collapsibleState: vscode.TreeItemCollapsibleState;
+ if (isDirectory) {
+ // Directories are always collapsible
+ collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
+ } else if (isSmellItem) {
+ // Smell items are never collapsible
+ collapsibleState = vscode.TreeItemCollapsibleState.None;
+ } else if (isOutdated) {
+ // Outdated files are not collapsible
+ collapsibleState = vscode.TreeItemCollapsibleState.None;
+ } else {
+ // Files with smells are collapsible
+ collapsibleState = hasSmells
+ ? vscode.TreeItemCollapsibleState.Collapsed
+ : vscode.TreeItemCollapsibleState.None;
+ }
+
+ const item = new vscode.TreeItem(path.basename(element), collapsibleState);
+
+ if (isDirectory) {
+ item.contextValue = 'ecoOptimizerFolder';
+ } else if (!isSmellItem) {
+ item.contextValue = 'ecoOptimizerFile';
+ this.assignOpenFileCommand(item, element);
+ this.updateFileItem(item, status, isOutdated);
+ } else {
+ item.contextValue = 'ecoOptimizerSmell';
+ const parentFile = this.stateManager.getFileForSmell(element);
+ if (parentFile) {
+ const [, lineStr] = element.split(': Line ');
+ const lines = lineStr.split(',').map((line) => parseInt(line.trim(), 10));
+ const firstLine = lines.length > 0 ? lines[0] - 1 : 0;
+ this.assignJumpToSmellCommand(item, parentFile, firstLine);
+ }
+ this.setSmellTooltip(item, element);
+ }
+
+ return item;
+ }
+
+ /**
+ * Assigns a command to open a file when the tree item is clicked.
+ * @param item - The tree item to update.
+ * @param filePath - The path of the file to open.
+ */
+ private assignOpenFileCommand(item: vscode.TreeItem, filePath: string): void {
+ item.command = {
+ command: 'ecooptimizer.openFile',
+ title: 'Open File',
+ arguments: [vscode.Uri.file(filePath)],
+ };
+ }
+
+ /**
+ * Updates the file item's status, including icon, message, and description.
+ * @param item - The tree item to update.
+ * @param status - The analysis status (e.g., "queued", "passed", "failed", "outdated").
+ * @param isOutdated - Whether the file is outdated.
+ */
+ private updateFileItem(
+ item: vscode.TreeItem,
+ status: string,
+ isOutdated: boolean,
+ ): void {
+ if (isOutdated) {
+ item.description = 'outdated';
+ item.iconPath = new vscode.ThemeIcon(
+ 'warning',
+ new vscode.ThemeColor('charts.orange'),
+ );
+ } else {
+ item.iconPath = this.getStatusIcon(status);
+ }
+ item.tooltip = `${path.basename(
+ item.label as string,
+ )} (${this.getStatusMessage(status)})`;
+ }
+
+ /**
+ * Assigns a command to jump to a specific line in a file when the tree item is clicked.
+ * @param item - The tree item to update.
+ * @param filePath - The path of the file containing the smell.
+ * @param line - The line number to jump to.
+ */
+ private assignJumpToSmellCommand(
+ item: vscode.TreeItem,
+ filePath: string,
+ line: number,
+ ): void {
+ item.command = {
+ command: 'ecooptimizer.jumpToSmell',
+ title: 'Jump to Smell',
+ arguments: [filePath, line],
+ };
+ }
+
+ /**
+ * Sets the tooltip for a smell item.
+ * @param item - The tree item to update.
+ * @param smellDescription - The description of the smell.
+ */
+ private setSmellTooltip(item: vscode.TreeItem, smellDescription: string): void {
+ item.tooltip = smellDescription;
+ }
+
+ /**
+ * Retrieves the appropriate VS Code icon based on the smell analysis status.
+ * @param status - The analysis status.
+ * @returns The corresponding VS Code theme icon.
+ */
+ private 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'),
+ );
+ default:
+ return new vscode.ThemeIcon('circle-outline');
+ }
+ }
+
+ /**
+ * Retrieves the status message corresponding to the smell analysis state.
+ * @param status - The analysis status.
+ * @returns A descriptive status message.
+ */
+ private 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';
+ default:
+ return 'Smells Not Yet Detected';
+ }
+ }
+}
diff --git a/src/providers/FilterSmellsProvider.ts b/src/providers/FilterSmellsProvider.ts
new file mode 100644
index 0000000..f5e975f
--- /dev/null
+++ b/src/providers/FilterSmellsProvider.ts
@@ -0,0 +1,181 @@
+import * as vscode from 'vscode';
+import { FilterSmellConfig, loadSmells, saveSmells } from '../utils/smellsData';
+
+/**
+ * Provides a tree view for filtering code smells within the VS Code extension.
+ */
+export class FilterSmellsProvider
+ implements vscode.TreeDataProvider
+{
+ 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) {
+ this.smells = loadSmells();
+ }
+
+ /**
+ * Associates a TreeView instance with the provider and listens for checkbox state changes.
+ * @param treeView - The TreeView instance.
+ */
+ setTreeView(treeView: vscode.TreeView): void {
+ this.treeView = treeView;
+
+ this.treeView.onDidChangeCheckboxState((event) => {
+ event.items.forEach((item) => {
+ if (item[0] instanceof SmellItem) {
+ this.toggleSmell(item[0].key);
+ }
+ });
+ });
+ }
+
+ /**
+ * Returns the tree item representation for a given element.
+ * @param element - The tree item element.
+ */
+ getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
+ return element;
+ }
+
+ /**
+ * Retrieves the child elements for a given tree item.
+ * If no parent element is provided, returns the list of smells.
+ * @param element - The parent tree item (optional).
+ */
+ getChildren(element?: SmellItem): Thenable {
+ if (!element) {
+ return Promise.resolve(
+ Object.keys(this.smells).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,
+ );
+ }),
+ );
+ }
+
+ 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 the enabled state of a specific smell and updates the configuration file.
+ * @param smellKey - The key of the smell to toggle.
+ */
+ toggleSmell(smellKey: string): void {
+ if (this.smells[smellKey]) {
+ this.smells[smellKey].enabled = !this.smells[smellKey].enabled;
+ saveSmells(this.smells);
+ this._onDidChangeTreeData.fire();
+ }
+ }
+
+ /**
+ * Updates the value of a specific smell option and saves the configuration.
+ * @param smellKey - The key of the smell.
+ * @param optionKey - The key of the option.
+ * @param newValue - The new value to set.
+ */
+ updateOption(
+ smellKey: string,
+ optionKey: string,
+ newValue: number | string,
+ ): void {
+ if (this.smells[smellKey]?.analyzer_options?.[optionKey]) {
+ this.smells[smellKey].analyzer_options[optionKey].value = newValue;
+ saveSmells(this.smells);
+ this._onDidChangeTreeData.fire();
+ } else {
+ vscode.window.showErrorMessage(
+ `Error: No analyzer option found for ${optionKey}`,
+ );
+ }
+ }
+
+ /**
+ * Refreshes the tree view, updating the UI.
+ */
+ refresh(): void {
+ this._onDidChangeTreeData.fire(undefined);
+ }
+
+ /**
+ * Enables or disables all smells in the filter and updates the configuration.
+ * @param enabled - Whether all smells should be enabled or disabled.
+ */
+ setAllSmellsEnabled(enabled: boolean): void {
+ Object.keys(this.smells).forEach((key) => {
+ this.smells[key].enabled = enabled;
+ });
+ saveSmells(this.smells);
+ this._onDidChangeTreeData.fire();
+ }
+}
+
+/**
+ * Represents a smell item in the tree 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;
+ }
+}
+
+/**
+ * Represents an option item for a smell in the tree view.
+ */
+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/SmellsViewProvider.ts b/src/providers/SmellsViewProvider.ts
new file mode 100644
index 0000000..95d48b3
--- /dev/null
+++ b/src/providers/SmellsViewProvider.ts
@@ -0,0 +1,130 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import * as path from 'path';
+import { SmellsStateManager } from '../managers/SmellsViewStateManager';
+import { SmellsUIManager } from '../managers/SmellsViewUIManager';
+
+export class SmellsDisplayProvider implements vscode.TreeDataProvider {
+ private _onDidChangeTreeData = new vscode.EventEmitter();
+ readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ private stateManager: SmellsStateManager;
+ private uiManager: SmellsUIManager;
+
+ constructor(private context: vscode.ExtensionContext) {
+ this.stateManager = new SmellsStateManager();
+ this.uiManager = new SmellsUIManager(this.stateManager);
+ }
+
+ /**
+ * Refreshes the tree view, triggering a UI update.
+ */
+ refresh(): void {
+ this._onDidChangeTreeData.fire(undefined);
+ }
+
+ /**
+ * Returns a tree item representing a file, folder, or detected smell.
+ * @param element - The file or folder path, or a detected smell.
+ */
+ getTreeItem(element: string): vscode.TreeItem {
+ return this.uiManager.createTreeItem(element);
+ }
+
+ /**
+ * Retrieves child elements for a given tree item.
+ * @param element - The parent tree item (optional).
+ */
+ async getChildren(element?: string): Promise {
+ if (!element) {
+ const configuredPath = this.context.workspaceState.get(
+ 'workspaceConfiguredPath',
+ );
+ return configuredPath ? [configuredPath] : [];
+ }
+
+ const isDirectory = fs.existsSync(element) && fs.statSync(element).isDirectory();
+
+ if (isDirectory) {
+ return fs
+ .readdirSync(element)
+ .filter((file) => file.endsWith('.py'))
+ .map((file) => path.join(element, file));
+ }
+
+ // Check if the file is outdated
+ if (this.stateManager.isFileOutdated(element)) {
+ return []; // Return an empty array if the file is outdated
+ }
+
+ // If the file is not outdated, return the detected smells
+ const smells = this.stateManager.getSmellsForFile(element);
+ return smells.map((smell) => {
+ const smellItem = `${smell.acronym}: Line ${smell.occurrences
+ .map((o) => o.line)
+ .join(', ')}`;
+ this.stateManager.mapSmellToFile(smellItem, element);
+ return smellItem;
+ });
+ }
+
+ /**
+ * Updates the detected smells for a file and refreshes the tree view.
+ * @param filePath - The analyzed file path.
+ * @param smells - The detected smells in the file.
+ * @param smellMetadata - Metadata containing message ID and acronym for each smell.
+ */
+ updateSmells(
+ filePath: string,
+ smells: Smell[],
+ smellMetadata: Record,
+ ): void {
+ this.stateManager.updateSmells(filePath, smells, smellMetadata);
+ this.refresh();
+ }
+
+ /**
+ * Marks a file as outdated, updating its appearance in the UI.
+ * @param filePath - The path of the modified file.
+ */
+ markFileAsOutdated(filePath: string): void {
+ this.stateManager.markFileAsOutdated(filePath);
+ this.refresh();
+ }
+
+ /**
+ * Updates the status of a specific file or folder.
+ * @param element - The file or folder path.
+ * @param status - The new status to set.
+ */
+ updateStatus(element: string, status: string): void {
+ this.stateManager.updateFileStatus(element, status);
+ this.refresh();
+ }
+
+ /**
+ * Clears the outdated status for a file.
+ * @param filePath - The path of the file to clear.
+ */
+ clearOutdatedStatus(filePath: string): void {
+ this.stateManager.clearOutdatedStatus(filePath);
+ this.refresh();
+ }
+
+ /**
+ * Checks if a file is marked as outdated.
+ * @param filePath - The path of the file to check.
+ * @returns `true` if the file is outdated, `false` otherwise.
+ */
+ isFileOutdated(filePath: string): boolean {
+ return this.stateManager.isFileOutdated(filePath);
+ }
+
+ /**
+ * Clears all detected smells and resets file statuses.
+ */
+ resetAllSmells(): void {
+ this.stateManager.resetAllSmells();
+ this.refresh();
+ }
+}
diff --git a/src/ui/fileHighlighter.ts b/src/ui/fileHighlighter.ts
index 1d95989..c620507 100644
--- a/src/ui/fileHighlighter.ts
+++ b/src/ui/fileHighlighter.ts
@@ -1,20 +1,23 @@
import * as vscode from 'vscode';
import { getEditor } from '../utils/editorUtils';
-import { ContextManager } from '../context/contextManager';
import { HoverManager } from './hoverManager';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
export class FileHighlighter {
private static instance: FileHighlighter;
- private contextManager: ContextManager;
private decorations: vscode.TextEditorDecorationType[] = [];
- private constructor(contextManager: ContextManager) {
- this.contextManager = contextManager;
- }
+ constructor(
+ private context: vscode.ExtensionContext,
+ private smellsCacheManager: SmellsCacheManager,
+ ) {}
- public static getInstance(contextManager: ContextManager): FileHighlighter {
+ public static getInstance(
+ context: vscode.ExtensionContext,
+ smellsCacheManager: SmellsCacheManager,
+ ): FileHighlighter {
if (!FileHighlighter.instance) {
- FileHighlighter.instance = new FileHighlighter(contextManager);
+ FileHighlighter.instance = new FileHighlighter(context, smellsCacheManager);
}
return FileHighlighter.instance;
}
@@ -71,7 +74,11 @@ export class FileHighlighter {
const indexEnd = lineText.trimEnd().length + 2;
const range = new vscode.Range(line, indexStart, line, indexEnd);
- const hoverManager = HoverManager.getInstance(this.contextManager, smells);
+ const hoverManager = HoverManager.getInstance(
+ this.context,
+ this.smellsCacheManager,
+ smells,
+ );
return { range, hoverMessage: hoverManager.hoverContent || undefined };
});
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
index 09df83e..621b539 100644
--- a/src/ui/hoverManager.ts
+++ b/src/ui/hoverManager.ts
@@ -3,17 +3,20 @@ import {
refactorSelectedSmell,
refactorAllSmellsOfType,
} from '../commands/refactorSmell';
-import { ContextManager } from '../context/contextManager';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
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 {
+ static getInstance(
+ context: vscode.ExtensionContext,
+ smellsCacheManager: SmellsCacheManager,
+ smells: Smell[],
+ ): HoverManager {
if (!HoverManager.instance) {
- HoverManager.instance = new HoverManager(contextManager, smells);
+ HoverManager.instance = new HoverManager(context, smellsCacheManager, smells);
} else {
HoverManager.instance.updateSmells(smells);
}
@@ -21,11 +24,11 @@ export class HoverManager {
}
public constructor(
- private contextManager: ContextManager,
+ private context: vscode.ExtensionContext,
+ private smellsCacheManager: SmellsCacheManager,
smells: Smell[],
) {
this.smells = smells || [];
- this.vscodeContext = contextManager.context;
this.hoverContent = this.registerHoverProvider() ?? new vscode.MarkdownString();
this.registerCommands();
}
@@ -36,7 +39,7 @@ export class HoverManager {
// Register hover provider for Python files
public registerHoverProvider(): void {
- this.vscodeContext.subscriptions.push(
+ this.context.subscriptions.push(
vscode.languages.registerHoverProvider(
{ scheme: 'file', language: 'python' },
{
@@ -94,20 +97,22 @@ export class HoverManager {
// Register commands for refactor actions
public registerCommands(): void {
- this.vscodeContext.subscriptions.push(
+ this.context.subscriptions.push(
vscode.commands.registerCommand(
'extension.refactorThisSmell',
async (smell: Smell) => {
- const contextManager = new ContextManager(this.vscodeContext);
- await refactorSelectedSmell(contextManager, smell);
+ await refactorSelectedSmell(this.context, this.smellsCacheManager, smell);
},
),
// 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);
+ await refactorAllSmellsOfType(
+ this.context,
+ this.smellsCacheManager,
+ smell.messageId,
+ );
},
),
);
diff --git a/src/ui/lineSelectionManager.ts b/src/ui/lineSelectionManager.ts
index 29340ab..a34ec0b 100644
--- a/src/ui/lineSelectionManager.ts
+++ b/src/ui/lineSelectionManager.ts
@@ -1,16 +1,10 @@
import * as vscode from 'vscode';
-import { ContextManager } from '../context/contextManager';
-import { envConfig } from '../utils/envConfig';
-import { SmellDetectRecord } from '../commands/detectSmells';
-import { hashContent } from '../utils/hashDocs';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
export class LineSelectionManager {
- private contextManager;
private decoration: vscode.TextEditorDecorationType | null = null;
- public constructor(contextManager: ContextManager) {
- this.contextManager = contextManager;
- }
+ public constructor(private smellsCacheManager: SmellsCacheManager) {}
public removeLastComment(): void {
if (this.decoration) {
@@ -27,15 +21,9 @@ export class LineSelectionManager {
}
const filePath = editor.document.fileName;
- const smellsDetectRecord = this.contextManager.getWorkspaceData(
- envConfig.SMELL_MAP_KEY!,
- )[filePath] as SmellDetectRecord;
-
- if (!smellsDetectRecord) {
- return;
- }
+ const smells = this.smellsCacheManager.getCachedSmells(filePath);
- if (smellsDetectRecord.hash !== hashContent(editor.document.getText())) {
+ if (!smells || smells.length === 0) {
return;
}
@@ -48,8 +36,6 @@ export class LineSelectionManager {
const selectedLine = selection.start.line;
console.log(`selection: ${selectedLine}`);
- const smells = smellsDetectRecord.smells;
-
const smellsAtLine = smells.filter((smell) => {
return smell.occurences[0].line === selectedLine + 1;
});
@@ -59,7 +45,6 @@ export class LineSelectionManager {
}
let comment;
-
if (smellsAtLine.length > 1) {
comment = `🍂 Smell: ${smellsAtLine[0].symbol} | (+${
smellsAtLine.length - 1
diff --git a/src/ui/refactorView.ts b/src/ui/refactorView.ts
deleted file mode 100644
index ebd9709..0000000
--- a/src/ui/refactorView.ts
+++ /dev/null
@@ -1,198 +0,0 @@
-import * as vscode from 'vscode';
-import path from 'path';
-import * as fs from 'fs';
-
-import { envConfig } from '../utils/envConfig';
-import { readFileSync } from 'fs';
-import { sidebarState } from '../utils/handleEditorChange';
-import { MultiRefactoredData } from '../commands/refactorSmell';
-
-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,
- // eslint-disable-next-line unused-imports/no-unused-vars
- context: vscode.WebviewViewResolveContext,
- _token: vscode.CancellationToken,
- ): void {
- 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(
- envConfig.CURRENT_REFACTOR_DATA_KEY!,
- );
-
- if (savedState) {
- this.updateView();
- return;
- }
- }
- });
-
- webviewView.onDidDispose(() => {
- console.log('Webview Disposed');
- });
-
- webviewView.webview.onDidReceiveMessage(async (message) => {
- switch (message.command) {
- case 'selectFile':
- 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();
- await this.closeViews();
- break;
- case 'reject':
- await this.closeViews();
- break;
- }
- });
- console.log('Initialized sidebar view');
- }
-
- async updateView(): Promise {
- console.log('Updating view');
- const refactoredData = this._context.workspaceState.get(
- envConfig.CURRENT_REFACTOR_DATA_KEY!,
- )!;
-
- 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): Promise {
- const diffView = this._context.workspaceState.get(
- envConfig.ACTIVE_DIFF_KEY!,
- )!;
-
- if (diffView.isOpen) {
- console.log('starting view');
- this._view!.show(true);
- this._view!.webview.postMessage({
- command: 'update',
- data: refactoredData,
- sep: path.sep,
- });
- } else {
- console.log('Gonna pause');
- this.pauseView();
- }
- }
-
- async pauseView(): Promise {
- console.log('pausing view');
- this._view!.webview.postMessage({ command: 'pause' });
- }
-
- async clearView(): Promise {
- await this._view?.webview.postMessage({ command: 'clear' });
- this._file_map = new Map();
-
- console.log('View cleared');
- }
-
- 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 async closeViews(): Promise {
- 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);
- }
-
- console.log('Closed views');
-
- await this._context.workspaceState.update(
- envConfig.CURRENT_REFACTOR_DATA_KEY!,
- undefined,
- );
- }
-
- private async applyRefactoring(): Promise {
- 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);
- console.log(`Applied refactoring to ${original.fsPath}`);
- }
- vscode.window.showInformationMessage('Refactoring applied successfully!');
- } catch (error) {
- console.error('Error applying refactoring:', error);
- }
- }
-}
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts
index 60bd31d..d55ccdc 100644
--- a/src/utils/envConfig.ts
+++ b/src/utils/envConfig.ts
@@ -8,8 +8,10 @@ export interface EnvConfig {
FILE_CHANGES_KEY?: string;
LAST_USED_SMELLS_KEY?: string;
CURRENT_REFACTOR_DATA_KEY?: string;
+ SMELL_CACHE_KEY?: string;
+ FILE_HASH_CACHE_KEY?: string;
ACTIVE_DIFF_KEY?: string;
- SMELL_LINTING_ENABLED_KEY: string;
+ SMELL_LINTING_ENABLED_KEY?: string;
}
export const envConfig: EnvConfig = {
@@ -19,6 +21,5 @@ 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',
+ SMELL_LINTING_ENABLED_KEY: process.env.SMELL_LINTING_ENABLED_KEY,
};
diff --git a/src/utils/handleEditorChange.ts b/src/utils/handleEditorChange.ts
deleted file mode 100644
index 123745c..0000000
--- a/src/utils/handleEditorChange.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-import * as vscode from 'vscode';
-import { setTimeout } from 'timers/promises';
-
-import { envConfig } from './envConfig';
-import { ContextManager } from '../context/contextManager';
-
-interface DiffInfo {
- original: vscode.Uri;
- modified: vscode.Uri;
-}
-
-export let sidebarState = { isOpening: false };
-
-export async function handleEditorChanges(
- contextManager: ContextManager,
- editors: readonly vscode.TextEditor[],
-): Promise {
- console.log('Detected visible editor change');
- const diffState = contextManager.getWorkspaceData(
- envConfig.ACTIVE_DIFF_KEY!,
- );
- const refactorData = contextManager.getWorkspaceData(
- envConfig.CURRENT_REFACTOR_DATA_KEY!,
- );
-
- if (sidebarState.isOpening) {
- return;
- }
-
- 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(envConfig.ACTIVE_DIFF_KEY!, diffState);
- await setTimeout(500);
- // vscode.commands.executeCommand(
- // 'ecooptimizer.pauseRefactorSidebar'
- // );
- return;
- }
- if (diffState.firstOpen) {
- diffState.firstOpen = false;
- contextManager.setWorkspaceData(envConfig.ACTIVE_DIFF_KEY!, 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(envConfig.ACTIVE_DIFF_KEY!, diffState);
- await setTimeout(500);
- vscode.commands.executeCommand('ecooptimizer.showRefactorSidebar');
- }
- console.log('Doing nothing');
-}
-
-function isDiffEditorOpen(
- editors: readonly vscode.TextEditor[],
- diffState: ActiveDiff,
-): boolean | undefined {
- 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/handleSmellSettings.ts b/src/utils/handleSmellSettings.ts
deleted file mode 100644
index 2fad66b..0000000
--- a/src/utils/handleSmellSettings.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-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;
-} {
- 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]),
- );
-}
-
-/**
- * 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,
-): void {
- 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 (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(/\b\w/g, (char) => char.toUpperCase());
-}
diff --git a/src/utils/hashDocs.ts b/src/utils/hashDocs.ts
deleted file mode 100644
index 3baa19c..0000000
--- a/src/utils/hashDocs.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-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,
-): Promise {
- 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.fileName} has changed since last save.`);
- lastSavedHashes[document.fileName] = currentHash;
- await contextManager.setWorkspaceData(
- envConfig.FILE_CHANGES_KEY!,
- lastSavedHashes,
- );
- }
-}
diff --git a/src/utils/smellDetails.ts b/src/utils/smellDetails.ts
deleted file mode 100644
index bcc2b8c..0000000
--- a/src/utils/smellDetails.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-export const SMELL_MAP: Map = new Map([
- [
- '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.',
- },
- ],
- [
- '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.',
- },
- ],
- [
- '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.",
- },
- ],
- [
- '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.',
- },
- ],
- [
- '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.',
- },
- ],
- [
- '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.',
- },
- ],
- [
- '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.',
- },
- ],
- [
- '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.',
- },
- ],
- [
- '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.',
- },
- ],
-]);
diff --git a/src/utils/smellsData.ts b/src/utils/smellsData.ts
new file mode 100644
index 0000000..14b6e72
--- /dev/null
+++ b/src/utils/smellsData.ts
@@ -0,0 +1,97 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import * as path from 'path';
+
+/**
+ * Defines the structure of the smell configuration in smells.json.
+ * Used by FilterSmellsProvider.ts (modifies JSON based on user input).
+ */
+export interface FilterSmellConfig {
+ name: string;
+ message_id: string;
+ acronym: string;
+ enabled: boolean;
+ analyzer_options?: Record<
+ string,
+ { label: string; description: string; value: number | string }
+ >;
+}
+
+/**
+ * Defines the structure of enabled smells sent to the backend.
+ */
+interface DetectSmellConfig {
+ message_id: string;
+ acronym: string;
+ options: Record;
+}
+
+/**
+ * Loads the full smells configuration from smells.json.
+ * @returns A dictionary of smells with their respective configuration.
+ */
+export function loadSmells(): Record {
+ const filePath = path.join(__dirname, '..', 'data', 'smells.json');
+
+ if (!fs.existsSync(filePath)) {
+ vscode.window.showErrorMessage(
+ 'Configuration file missing: smells.json could not be found.',
+ );
+ return {};
+ }
+
+ try {
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
+ } catch (error) {
+ vscode.window.showErrorMessage(
+ 'Error loading smells.json. Please check the file format.',
+ );
+ console.error('ERROR: Failed to parse smells.json', error);
+ return {};
+ }
+}
+
+/**
+ * Saves the smells configuration to smells.json.
+ * @param smells - The smells data to be saved.
+ */
+export function saveSmells(smells: Record): void {
+ const filePath = path.join(__dirname, '..', 'data', 'smells.json');
+ try {
+ fs.writeFileSync(filePath, JSON.stringify(smells, null, 2));
+ } catch (error) {
+ vscode.window.showErrorMessage('Error saving smells.json.');
+ console.error('ERROR: Failed to write smells.json', error);
+ }
+}
+
+/**
+ * Extracts enabled smells from the loaded configuration.
+ * @returns A dictionary of enabled smells formatted for backend processing.
+ */
+export function getEnabledSmells(): Record {
+ const smells = loadSmells();
+
+ return Object.fromEntries(
+ Object.entries(smells)
+ .filter(([, smell]) => smell.enabled)
+ .map(([smellKey, smellData]) => [
+ smellKey,
+ {
+ message_id: smellData.message_id,
+ acronym: smellData.acronym,
+ options: Object.fromEntries(
+ Object.entries(smellData.analyzer_options ?? {}).map(
+ ([optionKey, optionData]) => [
+ optionKey,
+ typeof optionData.value === 'string' ||
+ typeof optionData.value === 'number'
+ ? optionData.value
+ : String(optionData.value),
+ ],
+ ),
+ ),
+ },
+ ]),
+ );
+}
From d9fc39ea15fdf8f0807869e4448ff9557b151b61 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 00:41:34 -0400
Subject: [PATCH 082/215] Fixed package-lock.json
---
package-lock.json | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 0688744..1c722b8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -239,13 +239,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 +531,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"
From f0f3d92a108bc86616bc1cfd5d7842cd43853b04 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 01:49:45 -0400
Subject: [PATCH 083/215] temporarily moved out old logic + files (will slowly
add back in)
---
.env | 6 -
package.json | 130 +---------
src/api/backend.ts | 89 -------
src/commands/detectSmells.ts | 14 --
src/commands/refactorSmell.ts | 336 ++-----------------------
src/commands/showLogs.ts | 190 --------------
src/extension.ts | 151 -----------
src/global.d.ts | 29 ---
src/listeners/fileSaveListener.ts | 11 -
src/managers/SmellsViewStateManager.ts | 1 -
src/types.d.ts | 57 -----
src/ui/fileHighlighter.ts | 137 ----------
src/ui/hoverManager.ts | 120 ---------
src/ui/lineSelectionManager.ts | 79 ------
src/utils/configManager.ts | 68 -----
src/utils/editorUtils.ts | 22 --
src/utils/envConfig.ts | 14 +-
17 files changed, 21 insertions(+), 1433 deletions(-)
delete mode 100644 src/commands/showLogs.ts
delete mode 100644 src/types.d.ts
delete mode 100644 src/ui/fileHighlighter.ts
delete mode 100644 src/ui/hoverManager.ts
delete mode 100644 src/ui/lineSelectionManager.ts
delete mode 100644 src/utils/configManager.ts
delete mode 100644 src/utils/editorUtils.ts
diff --git a/.env b/.env
index dfd4ddf..f668a4c 100644
--- a/.env
+++ b/.env
@@ -1,9 +1,3 @@
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'
FILE_HASH_CACHE_KEY='fileHashCache'
SMELL_CACHE_KEY='smellCache'
-SMELL_LINTING_ENABLED_KEY='smellLintingEnabled'
diff --git a/package.json b/package.json
index d96c02f..56110de 100644
--- a/package.json
+++ b/package.json
@@ -71,11 +71,7 @@
"!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"
+ "!src/context/*"
]
},
"lint-staged": {
@@ -220,17 +216,6 @@
"command": "ecooptimizer.wipeWorkCache",
"title": "Clear Smells Cache",
"category": "Eco Optimizer"
- },
- {
- "command": "ecooptimizer.startLogging",
- "title": "Show Backend Logs",
- "category": "Eco",
- "enablement": "false"
- },
- {
- "command": "ecooptimizer.toggleSmellLinting",
- "title": "🍃 Toggle Smell Linting",
- "category": "Eco"
}
],
"menus": {
@@ -277,120 +262,7 @@
"when": "viewItem == ecoOptimizerFile",
"group": "inline"
}
- ],
- "editor/title": [
- {
- "command": "ecooptimizer.toggleSmellLinting",
- "group": "navigation",
- "when": "eco.smellLintingEnabled == false",
- "icon": {
- "light": "off.svg",
- "dark": "off.svg"
- }
- },
- {
- "command": "ecooptimizer.toggleSmellLinting",
- "group": "navigation",
- "when": "eco.smellLintingEnabled == true",
- "icon": {
- "light": "on.svg",
- "dark": "on.svg"
- }
- }
]
- },
- "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.smellColours": {
- "order": 1,
- "type": "object",
- "additionalProperties": false,
- "description": "Configure the highlight colours of each smell (css syntax).",
- "default": {
- "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": "string",
- "default": "lightblue"
- },
- "too-many-arguments": {
- "type": "string",
- "default": "lightcoral"
- },
- "long-lambda-expression": {
- "type": "string",
- "default": "mediumpurple"
- },
- "long-message-chain": {
- "type": "string",
- "default": "lightpink"
- },
- "cached-repeated-calls": {
- "type": "string",
- "default": "lightgreen"
- },
- "string-concat-loop": {
- "type": "string",
- "default": "lightsalmon"
- },
- "no-self-use": {
- "type": "string",
- "default": "lightcyan"
- },
- "use-a-generator": {
- "type": "string",
- "default": "yellow"
- }
- }
- },
- "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."
- }
- }
}
}
}
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 49aefc9..544ddea 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,4 +1,3 @@
-import * as vscode from 'vscode';
import { envConfig } from '../utils/envConfig';
import { serverStatus } from '../utils/serverStatus';
@@ -19,39 +18,6 @@ export async function checkServerStatus(): Promise {
}
}
-/**
- * Initializes and synchronizes logs with the backend.
- *
- * @param {string} log_dir - The directory where logs are stored.
- * @returns {Promise} - Returns `true` if the logs are successfully initialized and synchronized, otherwise throws an error.
- * @throws {Error} - Throws an error if the initialization fails due to network issues or backend errors.
- */
-export async function initLogs(log_dir: string): Promise {
- const url = `${BASE_URL}/logs/init`;
-
- try {
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ log_dir }),
- });
-
- if (!response.ok) {
- throw new Error(`Unable to initialize logging: ${response.statusText}`);
- }
-
- return true;
- } catch (error: any) {
- if (error instanceof Error) {
- throw new Error(`Eco: Unable to initialize logging: ${error.message}`);
- } else {
- throw new Error('Eco: An unexpected error occurred while initializing logs.');
- }
- }
-}
-
/**
* Sends a request to the backend to detect code smells in the specified file.
*
@@ -95,59 +61,4 @@ export async function fetchSmells(
`Failed to connect to the backend: ${error.message}. Please check your network and try again.`
);
}
-}
-
-
-/**
- * Refactors a specific code smell in a given file.
- *
- * @param {string} filePath - The path to the file containing the code smell.
- * @param {Smell} smell - The code smell to refactor.
- * @returns {Promise} - The result of the refactoring operation.
- * @throws {Error} - Throws an error if the workspace folder cannot be determined, the API request fails, or an unexpected error occurs.
- */
-export async function refactorSmell(
- filePath: string,
- smell: Smell,
-): Promise {
- const url = `${BASE_URL}/refactor`;
-
- const workspaceFolder = vscode.workspace.workspaceFolders?.find((folder) =>
- filePath.includes(folder.uri.fsPath),
- );
-
- if (!workspaceFolder) {
- throw new Error(`Unable to determine workspace folder for file: ${filePath}`);
- }
-
- const workspaceFolderPath = workspaceFolder.uri.fsPath;
-
- const payload = {
- source_dir: workspaceFolderPath,
- smell,
- };
-
- try {
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(payload),
- });
-
- if (!response.ok) {
- const errorText = await response.text();
- throw new Error(`Refactoring failed for smell "${smell.symbol}": ${errorText}`);
- }
-
- const refactorResult = (await response.json()) as RefactorOutput;
- return refactorResult;
- } catch (error) {
- if (error instanceof Error) {
- throw new Error(`Unexpected error during refactoring: ${error.message}`);
- } else {
- throw new Error('An unexpected error occurred during refactoring.');
- }
- }
}
\ No newline at end of file
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 5681670..3646d7a 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -30,7 +30,6 @@ export async function detectSmellsFile(
const filePath = typeof fileUri === 'string' ? fileUri : fileUri.fsPath;
// Handle outdated files before proceeding
- console.log('Handling outdated file:', filePath);
await handleOutdatedFile(filePath, smellsCacheManager, treeDataProvider);
// Open the file and compute its hash
@@ -38,11 +37,9 @@ export async function detectSmellsFile(
const fileContent = document.getText();
// Store the file hash after analyzing
- console.log('Storing file hash for:', filePath);
await smellsCacheManager.storeFileHash(filePath, fileContent);
// Retrieve enabled smells from configuration
- console.log('Retrieving enabled smells...');
const enabledSmells = getEnabledSmells();
// Ensure that at least one smell type is enabled
@@ -54,7 +51,6 @@ export async function detectSmellsFile(
}
// Check if smells are already cached
- console.log('Checking for cached smells...');
const cachedSmells = smellsCacheManager.getCachedSmells(filePath);
if (cachedSmells !== undefined) {
// Use cached smells if available
@@ -63,13 +59,11 @@ export async function detectSmellsFile(
);
if (cachedSmells.length > 0) {
- console.log('Updating UI with cached smells...');
treeDataProvider.updateSmells(filePath, cachedSmells, enabledSmells);
} else {
treeDataProvider.updateStatus(filePath, 'no_issues');
}
- console.log('Analysis complete: Using cached smells.');
return;
}
@@ -86,25 +80,21 @@ export async function detectSmellsFile(
try {
// Prepare enabled smells for backend request
- console.log('Preparing enabled smells for backend...');
const enabledSmellsForBackend = Object.fromEntries(
Object.entries(enabledSmells).map(([key, value]) => [key, value.options]),
);
// Request smell analysis from the backend
- console.log('Requesting smell analysis from the backend...');
const { smells, status } = await fetchSmells(filePath, enabledSmellsForBackend);
// Handle response and update UI
if (status === 200) {
// Cache detected smells, even if no smells are found
- console.log('Caching detected smells...');
await smellsCacheManager.setCachedSmells(filePath, smells);
// Remove the file from modifiedFiles after re-analysis
treeDataProvider.clearOutdatedStatus(filePath);
- console.log('Updating UI with detected smells...');
if (smells.length > 0) {
treeDataProvider.updateSmells(filePath, smells, enabledSmells);
vscode.window.showInformationMessage(
@@ -118,8 +108,6 @@ export async function detectSmellsFile(
`Analysis complete: No code smells found in ${path.basename(filePath)}.`,
);
}
-
- console.log('Analysis complete: Detected smells.');
} else {
throw new Error(`Unexpected status code: ${status}`);
}
@@ -143,7 +131,6 @@ export async function detectSmellsFolder(
treeDataProvider: SmellsDisplayProvider,
folderPath: string,
) {
- console.log('Detecting smells for all Python files in:', folderPath);
// Notify the user that folder analysis has started
vscode.window.showInformationMessage(
`Detecting code smells for all Python files in: ${path.basename(folderPath)}`,
@@ -176,7 +163,6 @@ export async function detectSmellsFolder(
// Analyze each Python file in the folder
for (const file of pythonFiles) {
- console.log('Analyzing:', file);
await detectSmellsFile(smellsCacheManager, treeDataProvider, file);
}
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index ffd323c..7919b7a 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -1,326 +1,26 @@
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 { FileHighlighter } from '../ui/fileHighlighter';
-import { serverStatus } from '../utils/serverStatus';
-import { ServerStatusType } from '../utils/serverStatus';
-import { SmellsCacheManager } from '../context/SmellsCacheManager';
-
-/* 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(
- context: vscode.ExtensionContext,
- smellsCacheManager: SmellsCacheManager,
- smellGiven?: Smell,
-): Promise {
- const { editor, filePath } = getEditorAndFilePath();
-
- const pastData = context.workspaceState.get(
- 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 = smellsCacheManager.getCachedSmells(filePath);
-
- 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.');
+import * as path from 'path';
+import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+
+// 📌 Refactor Code Smells for a File
+export async function refactorSmellsByType(
+ treeDataProvider: SmellsDisplayProvider,
+ fileUri: vscode.Uri | string,
+) {
+ if (!fileUri) {
+ vscode.window.showErrorMessage('Error: No file selected 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(context, editor, refactoredData);
-
- if (refactorResult.updatedSmells.length) {
- const fileHighlighter = FileHighlighter.getInstance(context, smellsCacheManager);
- 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
- context: vscode.ExtensionContext,
- // eslint-disable-next-line unused-imports/no-unused-vars
- smellsCacheManager: SmellsCacheManager,
- // 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(
- context: vscode.ExtensionContext,
- editor: vscode.TextEditor,
- refactoredData: RefactoredData | MultiRefactoredData,
-): Promise {
- // Store only the diff editor state
- await context.workspaceState.update(
- envConfig.CURRENT_REFACTOR_DATA_KEY!,
- refactoredData,
- );
-
+ const filePath = typeof fileUri === 'string' ? fileUri : fileUri.fsPath;
vscode.window.showInformationMessage(
- 'Hey Niv, this needs to be connected to the new refactor sidebar :)',
+ `Refactoring code smells in: ${path.basename(filePath)}`,
);
- // 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 });
- }
+ // Simulate backend request
+ setTimeout(() => {
+ vscode.window.showInformationMessage(
+ `Code smells refactored for: ${path.basename(filePath)}`,
+ );
+ }, 3000);
}
diff --git a/src/commands/showLogs.ts b/src/commands/showLogs.ts
deleted file mode 100644
index 93b5481..0000000
--- a/src/commands/showLogs.ts
+++ /dev/null
@@ -1,190 +0,0 @@
-import * as vscode from 'vscode';
-import WebSocket from 'ws';
-
-import { initLogs } from '../api/backend';
-import { envConfig } from '../utils/envConfig';
-import { serverStatus, ServerStatusType } from '../utils/serverStatus';
-
-const WEBSOCKET_BASE_URL = `ws://${envConfig.SERVER_URL}/logs`;
-
-class LogInitializationError extends Error {
- constructor(message: string) {
- super(message);
- this.name = 'LogInitializationError';
- }
-}
-
-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();
- }
- });
- }
-
- /**
- * 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 contextManager or logUri. Cannot initialize 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.');
- }
-
- this.initializeWebSockets();
- console.log('Successfully initialized WebSockets. Logging is now active.');
- return;
- } 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.');
- }
- }
- }
- }
-
- /**
- * 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');
- }
-
- /**
- * 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 },
- );
- }
- }
-
- /**
- * 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();
-
- console.log(logEvent);
- console.log('Level:', level);
-
- 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', (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`,
- );
- });
- }
-
- /**
- * 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/extension.ts b/src/extension.ts
index 940c227..9aaddc7 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -15,15 +15,6 @@ import { checkServerStatus } from './api/backend';
import { FilterSmellsProvider } from './providers/FilterSmellsProvider';
import { SmellsCacheManager } from './context/SmellsCacheManager';
import { registerFileSaveListener } from './listeners/fileSaveListener';
-import { serverStatus } from './utils/serverStatus';
-import {
- refactorAllSmellsOfType,
- refactorSelectedSmell,
-} from './commands/refactorSmell';
-import { LogManager } from './commands/showLogs';
-import { LineSelectionManager } from './ui/lineSelectionManager';
-
-let logManager: LogManager;
/**
* Activates the Eco-Optimizer extension and registers all necessary commands, providers, and listeners.
@@ -32,9 +23,6 @@ let logManager: LogManager;
export function activate(context: vscode.ExtensionContext): void {
console.log('Activating Eco-Optimizer extension...');
- // Iniiialize the log manager
- logManager = new LogManager(context);
-
// Initialize the SmellsCacheManager for managing caching of smells and file hashes.
const smellsCacheManager = new SmellsCacheManager(context);
@@ -104,44 +92,6 @@ export function activate(context: vscode.ExtensionContext): void {
),
);
- // 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(context, smellsCacheManager);
- } else {
- vscode.window.showWarningMessage('Action blocked: Server is down.');
- }
- }),
- );
-
- // Refactor All Smells of Type Command
- context.subscriptions.push(
- vscode.commands.registerCommand(
- 'ecooptimizer.refactorAllSmellsOfType',
- async (smellId: string) => {
- if (serverStatus.getStatus() === 'up') {
- console.log(
- `Eco: Refactor All Smells of Type Command Triggered for ${smellId}`,
- );
- refactorAllSmellsOfType(context, smellsCacheManager, smellId);
- } else {
- vscode.window.showWarningMessage('Action blocked: Server is down.');
- }
- },
- ),
- );
-
- // Register the "Toggle Smell Auto Lint" command.
- // TODO: Uncomment this block after implementing smell linting
- // context.subscriptions.push(
- // vscode.commands.registerCommand('ecooptimizer.toggleSmellLinting', () => {
- // console.log('Eco: Toggle Smell Linting Command Triggered');
- // toggleSmellLinting(contextManager);
- // }),
- // );
-
// Register the "Jump to Smell" command.
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.jumpToSmell', jumpToSmell),
@@ -154,112 +104,12 @@ export function activate(context: vscode.ExtensionContext): void {
}),
);
- // Adds comments to lines describing the smell
- const lineSelectManager = new LineSelectionManager(smellsCacheManager);
- context.subscriptions.push(
- vscode.window.onDidChangeTextEditorSelection((event) => {
- console.log('Eco: Detected line selection event');
- lineSelectManager.commentLine(event.textEditor);
- }),
- );
-
- // Register a listener for configuration changes
- context.subscriptions.push(
- vscode.workspace.onDidChangeConfiguration((event) => {
- handleConfigurationChange(event);
- }),
- );
-
- // Listen for editor changes
- // TODO: Uncomment this block after implementing smell linting
- // 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 = context.workspaceState.get(
- // envConfig.SMELL_LINTING_ENABLED_KEY,
- // false,
- // );
- // if (isEnabled) {
- // console.log('Eco: Smell linting is enabled. Detecting smells...');
- // await detectSmells(contextManager);
- // }
- // }
- // }
- // }),
- // );
-
// Register the file save listener to detect outdated files.
const fileSaveListener = registerFileSaveListener(
smellsCacheManager,
smellsDisplayProvider,
);
context.subscriptions.push(fileSaveListener);
-
- // TODO: Setting to re-enable popup if disabled
- const settingsPopupChoice = context.globalState.get('showSettingsPopup');
-
- if (settingsPopupChoice === undefined || settingsPopupChoice) {
- showSettingsPopup(context);
- }
-}
-
-function showSettingsPopup(context: vscode.ExtensionContext): 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') {
- context.globalState.update('showSettingsPopup', false);
- vscode.window.showInformationMessage(
- 'You can re-enable this popup again in the settings.',
- );
- }
- });
- }
-}
-
-function handleConfigurationChange(event: vscode.ConfigurationChangeEvent): void {
- // Check if any relevant setting was changed
- if (
- event.affectsConfiguration('ecooptimizer.projectWorkspacePath') ||
- 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.',
- );
- }
}
/**
@@ -267,5 +117,4 @@ function handleConfigurationChange(event: vscode.ConfigurationChangeEvent): void
*/
export function deactivate(): void {
console.log('Deactivating Eco-Optimizer extension...');
- logManager.stopWatchingLogs();
}
diff --git a/src/global.d.ts b/src/global.d.ts
index cf5f436..ae62978 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -33,33 +33,4 @@ declare global {
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;
- };
-
}
diff --git a/src/listeners/fileSaveListener.ts b/src/listeners/fileSaveListener.ts
index ae07102..b38d2d0 100644
--- a/src/listeners/fileSaveListener.ts
+++ b/src/listeners/fileSaveListener.ts
@@ -30,17 +30,6 @@ export function registerFileSaveListener(
)}" has been modified since the last analysis.`,
);
- // Check if smell linting is enabled
- // TODO: Uncomment this block after implementing smell linting
- // const isEnabled = contextManager.getWorkspaceData(
- // envConfig.SMELL_LINTING_ENABLED_KEY,
- // false,
- // );
- // if (isEnabled) {
- // console.log('Eco: Smell linting is enabled. Detecting smells...');
- // await detectSmells(contextManager);
- // }
-
// Mark file as outdated in the UI
smellsDisplayProvider.markFileAsOutdated(filePath);
}
diff --git a/src/managers/SmellsViewStateManager.ts b/src/managers/SmellsViewStateManager.ts
index 1e56c31..136751c 100644
--- a/src/managers/SmellsViewStateManager.ts
+++ b/src/managers/SmellsViewStateManager.ts
@@ -30,7 +30,6 @@ export class SmellsStateManager {
): void {
this.fileStatusMap.set(filePath, 'passed');
- console.log('Smells:', smells, typeof smells);
const formattedSmells: ProcessedSmell[] = smells.map((smell) => {
const foundEntry = Object.values(smellMetadata).find(
(smellData) => smellData.message_id === smell.messageId,
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
deleted file mode 100644
index c620507..0000000
--- a/src/ui/fileHighlighter.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import * as vscode from 'vscode';
-import { getEditor } from '../utils/editorUtils';
-import { HoverManager } from './hoverManager';
-import { SmellsCacheManager } from '../context/SmellsCacheManager';
-
-export class FileHighlighter {
- private static instance: FileHighlighter;
- private decorations: vscode.TextEditorDecorationType[] = [];
-
- constructor(
- private context: vscode.ExtensionContext,
- private smellsCacheManager: SmellsCacheManager,
- ) {}
-
- public static getInstance(
- context: vscode.ExtensionContext,
- smellsCacheManager: SmellsCacheManager,
- ): FileHighlighter {
- if (!FileHighlighter.instance) {
- FileHighlighter.instance = new FileHighlighter(context, smellsCacheManager);
- }
- return FileHighlighter.instance;
- }
-
- public resetHighlights(): void {
- this.decorations.forEach((decoration) => decoration.dispose());
- this.decorations = [];
- }
-
- public highlightSmells(editor: vscode.TextEditor, smells: Smell[]): 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(
- '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) => {
- const smellConfig = smellsConfig[smellType];
- if (smellConfig?.enabled) {
- const colour = useSingleColour ? singleHighlightColour : smellConfig.colour;
- this.highlightSmell(editor, smells, smellType, colour, highlightStyle);
- }
- });
- }
-
- 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.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 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.context,
- this.smellsCacheManager,
- smells,
- );
- return { range, hoverMessage: hoverManager.hoverContent || undefined };
- });
-
- console.log('Highlighting smell:', targetSmell, colour, style, smellLines);
- const decoration = this.getDecoration(colour, style);
- editor.setDecorations(decoration, smellLines);
- this.decorations.push(decoration);
- }
-
- 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`,
- });
- }
- }
-}
-
-function isValidLine(line: any): boolean {
- return (
- line !== undefined &&
- line !== null &&
- typeof line === 'number' &&
- Number.isFinite(line) &&
- line > 0 &&
- Number.isInteger(line) &&
- line <= getEditor()!.document.lineCount
- );
-}
diff --git a/src/ui/hoverManager.ts b/src/ui/hoverManager.ts
deleted file mode 100644
index 621b539..0000000
--- a/src/ui/hoverManager.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-import * as vscode from 'vscode';
-import {
- refactorSelectedSmell,
- refactorAllSmellsOfType,
-} from '../commands/refactorSmell';
-import { SmellsCacheManager } from '../context/SmellsCacheManager';
-
-export class HoverManager {
- private static instance: HoverManager;
- private smells: Smell[];
- public hoverContent: vscode.MarkdownString;
-
- static getInstance(
- context: vscode.ExtensionContext,
- smellsCacheManager: SmellsCacheManager,
- smells: Smell[],
- ): HoverManager {
- if (!HoverManager.instance) {
- HoverManager.instance = new HoverManager(context, smellsCacheManager, smells);
- } else {
- HoverManager.instance.updateSmells(smells);
- }
- return HoverManager.instance;
- }
-
- public constructor(
- private context: vscode.ExtensionContext,
- private smellsCacheManager: SmellsCacheManager,
- smells: Smell[],
- ) {
- this.smells = smells || [];
- this.hoverContent = this.registerHoverProvider() ?? new vscode.MarkdownString();
- this.registerCommands();
- }
-
- public updateSmells(smells: Smell[]): void {
- this.smells = smells || [];
- }
-
- // Register hover provider for Python files
- public registerHoverProvider(): void {
- this.context.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
- console.log('line number: ' + position.line);
- // 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),
- ),
- );
-
- console.log('smells: ' + smellsOnLine);
-
- 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),
- )})\t\t` +
- `---[Refactor all smells of this type...](command:extension.refactorAllSmellsOfType?${encodeURIComponent(
- JSON.stringify(smell),
- )})\n\n`,
- );
- console.log(hoverContent);
- });
-
- return hoverContent;
- }
-
- // Register commands for refactor actions
- public registerCommands(): void {
- this.context.subscriptions.push(
- vscode.commands.registerCommand(
- 'extension.refactorThisSmell',
- async (smell: Smell) => {
- await refactorSelectedSmell(this.context, this.smellsCacheManager, smell);
- },
- ),
- // clicking "Refactor All Smells of this Type..."
- vscode.commands.registerCommand(
- 'extension.refactorAllSmellsOfType',
- async (smell: Smell) => {
- await refactorAllSmellsOfType(
- this.context,
- this.smellsCacheManager,
- smell.messageId,
- );
- },
- ),
- );
- }
-}
diff --git a/src/ui/lineSelectionManager.ts b/src/ui/lineSelectionManager.ts
deleted file mode 100644
index a34ec0b..0000000
--- a/src/ui/lineSelectionManager.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import * as vscode from 'vscode';
-import { SmellsCacheManager } from '../context/SmellsCacheManager';
-
-export class LineSelectionManager {
- private decoration: vscode.TextEditorDecorationType | null = null;
-
- public constructor(private smellsCacheManager: SmellsCacheManager) {}
-
- public removeLastComment(): void {
- if (this.decoration) {
- console.log('Removing decoration');
- this.decoration.dispose();
- }
- }
-
- public commentLine(editor: vscode.TextEditor): void {
- this.removeLastComment();
-
- if (!editor) {
- return;
- }
-
- const filePath = editor.document.fileName;
- const smells = this.smellsCacheManager.getCachedSmells(filePath);
-
- if (!smells || smells.length === 0) {
- return;
- }
-
- const { selection } = editor;
-
- if (!selection.isSingleLine) {
- return;
- }
-
- const selectedLine = selection.start.line;
- console.log(`selection: ${selectedLine}`);
-
- const smellsAtLine = smells.filter((smell) => {
- return smell.occurences[0].line === selectedLine + 1;
- });
-
- if (smellsAtLine.length === 0) {
- return;
- }
-
- let comment;
- if (smellsAtLine.length > 1) {
- comment = `🍂 Smell: ${smellsAtLine[0].symbol} | (+${
- smellsAtLine.length - 1
- })`;
- } else {
- comment = `🍂 Smell: ${smellsAtLine[0].symbol}`;
- }
-
- this.decoration = vscode.window.createTextEditorDecorationType({
- isWholeLine: true,
- after: {
- contentText: comment,
- color: 'rgb(153, 211, 212)',
- margin: '0 0 0 10px',
- textDecoration: 'none',
- },
- });
-
- 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/configManager.ts b/src/utils/configManager.ts
deleted file mode 100644
index 45e49f5..0000000
--- a/src/utils/configManager.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-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')
- .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 logs output path
- static getLogsOutputPath(): string {
- const rawPath = vscode.workspace
- .getConfiguration('ecooptimizer')
- .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): void {
- vscode.workspace.onDidChangeConfiguration((event) => {
- if (
- event.affectsConfiguration('ecooptimizer.projectWorkspacePath') ||
- event.affectsConfiguration('ecooptimizer.logsOutputPath')
- ) {
- callback();
- }
- });
- }
-
- // write settings to both User and Workspace if necessary
- private static writeSetting(setting: string, value: string): void {
- const config = vscode.workspace.getConfiguration('ecooptimizer');
-
- // 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);
- }
- }
-}
diff --git a/src/utils/editorUtils.ts b/src/utils/editorUtils.ts
deleted file mode 100644
index e15d1ca..0000000
--- a/src/utils/editorUtils.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-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 }}
- * 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 };
-}
-
-/**
- * Gets the active editor if an editor is open.
- */
-export function getEditor(): vscode.TextEditor | undefined {
- return vscode.window.activeTextEditor;
-}
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts
index d55ccdc..7b98c34 100644
--- a/src/utils/envConfig.ts
+++ b/src/utils/envConfig.ts
@@ -4,22 +4,12 @@ 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;
SMELL_CACHE_KEY?: string;
FILE_HASH_CACHE_KEY?: string;
- ACTIVE_DIFF_KEY?: string;
- SMELL_LINTING_ENABLED_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,
- 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,
+ SMELL_CACHE_KEY: process.env.SMELL_CACHE_KEY,
+ FILE_HASH_CACHE_KEY: process.env.FILE_HASH_CACHE_KEY,
};
From ccf01d714f6be152d22ce133e2924cdc24998566 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 02:13:27 -0400
Subject: [PATCH 084/215] renamed to FilterViewProvider.ts
---
src/commands/filterSmells.ts | 2 +-
src/extension.ts | 2 +-
.../{FilterSmellsProvider.ts => FilterViewProvider.ts} | 0
3 files changed, 2 insertions(+), 2 deletions(-)
rename src/providers/{FilterSmellsProvider.ts => FilterViewProvider.ts} (100%)
diff --git a/src/commands/filterSmells.ts b/src/commands/filterSmells.ts
index 590ca6e..febfa92 100644
--- a/src/commands/filterSmells.ts
+++ b/src/commands/filterSmells.ts
@@ -1,5 +1,5 @@
import * as vscode from 'vscode';
-import { FilterSmellsProvider } from '../providers/FilterSmellsProvider';
+import { FilterSmellsProvider } from '../providers/FilterViewProvider';
/**
* Registers VS Code commands for managing smell filters.
diff --git a/src/extension.ts b/src/extension.ts
index 9aaddc7..bdeb6f1 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -12,7 +12,7 @@ import { jumpToSmell } from './commands/jumpToSmell';
import { wipeWorkCache } from './commands/wipeWorkCache';
import { SmellsDisplayProvider } from './providers/SmellsViewProvider';
import { checkServerStatus } from './api/backend';
-import { FilterSmellsProvider } from './providers/FilterSmellsProvider';
+import { FilterSmellsProvider } from './providers/FilterViewProvider';
import { SmellsCacheManager } from './context/SmellsCacheManager';
import { registerFileSaveListener } from './listeners/fileSaveListener';
diff --git a/src/providers/FilterSmellsProvider.ts b/src/providers/FilterViewProvider.ts
similarity index 100%
rename from src/providers/FilterSmellsProvider.ts
rename to src/providers/FilterViewProvider.ts
From 89e9320437d493cd94b124fe54084f91c495fb0e Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 02:16:04 -0400
Subject: [PATCH 085/215] renamed to SmellsViewProvider class
---
src/commands/detectSmells.ts | 8 ++++----
src/commands/refactorSmell.ts | 4 ++--
src/commands/resetConfiguration.ts | 4 ++--
src/commands/wipeWorkCache.ts | 4 ++--
src/context/SmellsCacheManager.ts | 4 ++--
src/extension.ts | 4 ++--
src/listeners/fileSaveListener.ts | 4 ++--
src/providers/SmellsViewProvider.ts | 2 +-
8 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 3646d7a..edde8e6 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
import { fetchSmells } from '../api/backend';
-import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+import { SmellsViewProvider } from '../providers/SmellsViewProvider';
import { getEnabledSmells } from '../utils/smellsData';
import { SmellsCacheManager } from '../context/SmellsCacheManager';
import { serverStatus, ServerStatusType } from '../utils/serverStatus';
@@ -17,7 +17,7 @@ import { serverStatus, ServerStatusType } from '../utils/serverStatus';
*/
export async function detectSmellsFile(
smellsCacheManager: SmellsCacheManager,
- treeDataProvider: SmellsDisplayProvider,
+ treeDataProvider: SmellsViewProvider,
fileUri: vscode.Uri | string,
) {
// Validate the file URI or path
@@ -128,7 +128,7 @@ export async function detectSmellsFile(
*/
export async function detectSmellsFolder(
smellsCacheManager: SmellsCacheManager,
- treeDataProvider: SmellsDisplayProvider,
+ treeDataProvider: SmellsViewProvider,
folderPath: string,
) {
// Notify the user that folder analysis has started
@@ -181,7 +181,7 @@ export async function detectSmellsFolder(
async function handleOutdatedFile(
filePath: string,
smellsCacheManager: SmellsCacheManager,
- smellsDisplayProvider: SmellsDisplayProvider,
+ smellsDisplayProvider: SmellsViewProvider,
) {
// Check if the file is marked as outdated
if (smellsDisplayProvider.isFileOutdated(filePath)) {
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 7919b7a..7a0cbfe 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -1,10 +1,10 @@
import * as vscode from 'vscode';
import * as path from 'path';
-import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+import { SmellsViewProvider } from '../providers/SmellsViewProvider';
// 📌 Refactor Code Smells for a File
export async function refactorSmellsByType(
- treeDataProvider: SmellsDisplayProvider,
+ treeDataProvider: SmellsViewProvider,
fileUri: vscode.Uri | string,
) {
if (!fileUri) {
diff --git a/src/commands/resetConfiguration.ts b/src/commands/resetConfiguration.ts
index fdd2305..945b7bc 100644
--- a/src/commands/resetConfiguration.ts
+++ b/src/commands/resetConfiguration.ts
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
import { SmellsCacheManager } from '../context/SmellsCacheManager'; // Updated import
-import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+import { SmellsViewProvider } from '../providers/SmellsViewProvider';
/**
* Resets the workspace configuration by clearing the stored path and wiping cached smells.
@@ -13,7 +13,7 @@ import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
export async function resetConfiguration(
context: vscode.ExtensionContext,
smellsCacheManager: SmellsCacheManager,
- treeDataProvider: SmellsDisplayProvider,
+ treeDataProvider: SmellsViewProvider,
) {
const confirm = await vscode.window.showWarningMessage(
'Are you sure you want to reset the workspace configuration? This will remove the currently selected folder and wipe cached smells.',
diff --git a/src/commands/wipeWorkCache.ts b/src/commands/wipeWorkCache.ts
index 768c780..e8c6aa8 100644
--- a/src/commands/wipeWorkCache.ts
+++ b/src/commands/wipeWorkCache.ts
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
import { SmellsCacheManager } from '../context/SmellsCacheManager';
-import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+import { SmellsViewProvider } from '../providers/SmellsViewProvider';
/**
* Clears the smells cache and refreshes the UI.
@@ -9,7 +9,7 @@ import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
*/
export async function wipeWorkCache(
smellsCacheManager: SmellsCacheManager,
- smellsDisplayProvider: SmellsDisplayProvider,
+ smellsDisplayProvider: SmellsViewProvider,
) {
const userResponse = await vscode.window.showWarningMessage(
'Are you sure you want to clear the smells cache? This action cannot be undone.',
diff --git a/src/context/SmellsCacheManager.ts b/src/context/SmellsCacheManager.ts
index b015ea9..e425db9 100644
--- a/src/context/SmellsCacheManager.ts
+++ b/src/context/SmellsCacheManager.ts
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
import { createHash } from 'crypto';
-import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+import { SmellsViewProvider } from '../providers/SmellsViewProvider';
import { envConfig } from '../utils/envConfig';
/**
@@ -122,7 +122,7 @@ export class SmellsCacheManager {
* @param smellsDisplayProvider - The tree view provider responsible for the UI.
*/
public async clearCacheAndRefreshUI(
- smellsDisplayProvider: SmellsDisplayProvider,
+ smellsDisplayProvider: SmellsViewProvider,
): Promise {
// Remove all cached smells from the workspace state
await this.clearSmellsCache();
diff --git a/src/extension.ts b/src/extension.ts
index bdeb6f1..291efaa 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -10,7 +10,7 @@ import { openFile } from './commands/openFile';
import { registerFilterSmellCommands } from './commands/filterSmells';
import { jumpToSmell } from './commands/jumpToSmell';
import { wipeWorkCache } from './commands/wipeWorkCache';
-import { SmellsDisplayProvider } from './providers/SmellsViewProvider';
+import { SmellsViewProvider } from './providers/SmellsViewProvider';
import { checkServerStatus } from './api/backend';
import { FilterSmellsProvider } from './providers/FilterViewProvider';
import { SmellsCacheManager } from './context/SmellsCacheManager';
@@ -27,7 +27,7 @@ export function activate(context: vscode.ExtensionContext): void {
const smellsCacheManager = new SmellsCacheManager(context);
// Initialize the Code Smells View.
- const smellsDisplayProvider = new SmellsDisplayProvider(context);
+ const smellsDisplayProvider = new SmellsViewProvider(context);
const codeSmellsView = vscode.window.createTreeView('ecooptimizer.view', {
treeDataProvider: smellsDisplayProvider,
});
diff --git a/src/listeners/fileSaveListener.ts b/src/listeners/fileSaveListener.ts
index b38d2d0..63b3199 100644
--- a/src/listeners/fileSaveListener.ts
+++ b/src/listeners/fileSaveListener.ts
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
import { SmellsCacheManager } from '../context/SmellsCacheManager';
-import { SmellsDisplayProvider } from '../providers/SmellsViewProvider';
+import { SmellsViewProvider } from '../providers/SmellsViewProvider';
import path from 'path';
/**
@@ -10,7 +10,7 @@ import path from 'path';
*/
export function registerFileSaveListener(
smellsCacheManager: SmellsCacheManager,
- smellsDisplayProvider: SmellsDisplayProvider,
+ smellsDisplayProvider: SmellsViewProvider,
): vscode.Disposable {
return vscode.workspace.onDidSaveTextDocument(async (document) => {
const filePath = document.fileName;
diff --git a/src/providers/SmellsViewProvider.ts b/src/providers/SmellsViewProvider.ts
index 95d48b3..866ef36 100644
--- a/src/providers/SmellsViewProvider.ts
+++ b/src/providers/SmellsViewProvider.ts
@@ -4,7 +4,7 @@ import * as path from 'path';
import { SmellsStateManager } from '../managers/SmellsViewStateManager';
import { SmellsUIManager } from '../managers/SmellsViewUIManager';
-export class SmellsDisplayProvider implements vscode.TreeDataProvider {
+export class SmellsViewProvider implements vscode.TreeDataProvider {
private _onDidChangeTreeData = new vscode.EventEmitter();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
From 2495e3324c14be566c9b871537f8757be84acbb1 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 02:16:50 -0400
Subject: [PATCH 086/215] renamed to FilterViewProvider class
---
src/commands/filterSmells.ts | 4 ++--
src/extension.ts | 4 ++--
src/providers/FilterViewProvider.ts | 4 +---
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/commands/filterSmells.ts b/src/commands/filterSmells.ts
index febfa92..a3dd7d0 100644
--- a/src/commands/filterSmells.ts
+++ b/src/commands/filterSmells.ts
@@ -1,5 +1,5 @@
import * as vscode from 'vscode';
-import { FilterSmellsProvider } from '../providers/FilterViewProvider';
+import { FilterViewProvider } from '../providers/FilterViewProvider';
/**
* Registers VS Code commands for managing smell filters.
@@ -8,7 +8,7 @@ import { FilterSmellsProvider } from '../providers/FilterViewProvider';
*/
export function registerFilterSmellCommands(
context: vscode.ExtensionContext,
- filterSmellsProvider: FilterSmellsProvider,
+ filterSmellsProvider: FilterViewProvider,
) {
/**
* Toggles the state of a specific smell filter.
diff --git a/src/extension.ts b/src/extension.ts
index 291efaa..da67ee9 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -12,7 +12,7 @@ import { jumpToSmell } from './commands/jumpToSmell';
import { wipeWorkCache } from './commands/wipeWorkCache';
import { SmellsViewProvider } from './providers/SmellsViewProvider';
import { checkServerStatus } from './api/backend';
-import { FilterSmellsProvider } from './providers/FilterViewProvider';
+import { FilterViewProvider } from './providers/FilterViewProvider';
import { SmellsCacheManager } from './context/SmellsCacheManager';
import { registerFileSaveListener } from './listeners/fileSaveListener';
@@ -61,7 +61,7 @@ export function activate(context: vscode.ExtensionContext): void {
);
// Initialize the Filter Smells View.
- const filterSmellsProvider = new FilterSmellsProvider(context);
+ const filterSmellsProvider = new FilterViewProvider(context);
const filterSmellsView = vscode.window.createTreeView('ecooptimizer.filterView', {
treeDataProvider: filterSmellsProvider,
showCollapseAll: true,
diff --git a/src/providers/FilterViewProvider.ts b/src/providers/FilterViewProvider.ts
index f5e975f..d8a416a 100644
--- a/src/providers/FilterViewProvider.ts
+++ b/src/providers/FilterViewProvider.ts
@@ -4,9 +4,7 @@ import { FilterSmellConfig, loadSmells, saveSmells } from '../utils/smellsData';
/**
* Provides a tree view for filtering code smells within the VS Code extension.
*/
-export class FilterSmellsProvider
- implements vscode.TreeDataProvider
-{
+export class FilterViewProvider implements vscode.TreeDataProvider {
private _onDidChangeTreeData: vscode.EventEmitter<
vscode.TreeItem | undefined | void
> = new vscode.EventEmitter();
From 160d55d428bdcb13d9ce2664fb067c18444b261b Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 02:17:31 -0400
Subject: [PATCH 087/215] cleaning
---
src/commands/toggleSmellLinting.ts | 49 ------------------------------
1 file changed, 49 deletions(-)
delete mode 100644 src/commands/toggleSmellLinting.ts
diff --git a/src/commands/toggleSmellLinting.ts b/src/commands/toggleSmellLinting.ts
deleted file mode 100644
index c605645..0000000
--- a/src/commands/toggleSmellLinting.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-// import * as vscode from 'vscode';
-// import { ContextManager } from '../context/contextManager';
-// 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,
-// );
-// }
-// }
From 7085de046054d5c94d4f10c1b580846ee9f92a5b Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 02:48:32 -0400
Subject: [PATCH 088/215] some cleaning in SmellsCacheManager.ts
---
src/context/SmellsCacheManager.ts | 50 ++++++++++++++++---------------
1 file changed, 26 insertions(+), 24 deletions(-)
diff --git a/src/context/SmellsCacheManager.ts b/src/context/SmellsCacheManager.ts
index e425db9..f1239e8 100644
--- a/src/context/SmellsCacheManager.ts
+++ b/src/context/SmellsCacheManager.ts
@@ -21,12 +21,8 @@ export class SmellsCacheManager {
* @returns An array of detected smells or `undefined` if the file has not been analyzed.
*/
public getCachedSmells(filePath: string): Smell[] | undefined {
- const cache = this.context.workspaceState.get>(
- envConfig.SMELL_CACHE_KEY!,
- {},
- );
-
- return cache[filePath]; // Returns an array of smells or `undefined` if not cached
+ const cache = this.getFullSmellCache();
+ return cache[filePath]; // May be undefined
}
/**
@@ -37,13 +33,8 @@ export class SmellsCacheManager {
* @param smells - The detected smells to store (empty array if no smells found).
*/
public async setCachedSmells(filePath: string, smells: Smell[]): Promise {
- const cache = this.context.workspaceState.get>(
- envConfig.SMELL_CACHE_KEY!,
- {},
- );
-
- cache[filePath] = smells; // Store detected smells or an empty array if clean
-
+ const cache = this.getFullSmellCache();
+ cache[filePath] = smells;
await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
}
@@ -61,14 +52,20 @@ export class SmellsCacheManager {
* @param filePath - The path of the file to clear.
*/
public async clearCachedSmellsForFile(filePath: string): Promise {
- const cache = this.context.workspaceState.get>(
+ const cache = this.getFullSmellCache();
+ delete cache[filePath];
+ await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
+ }
+
+ /**
+ * Retrieves the entire smell cache.
+ * @returns A record of file paths to cached smells.
+ */
+ public getFullSmellCache(): Record {
+ return this.context.workspaceState.get>(
envConfig.SMELL_CACHE_KEY!,
{},
);
-
- delete cache[filePath]; // Remove the file's cached smells
-
- await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
}
// ============================
@@ -90,10 +87,7 @@ export class SmellsCacheManager {
* @param hash - The computed file hash.
*/
public async storeFileHash(filePath: string, hash: string): Promise {
- const hashes = this.context.workspaceState.get>(
- envConfig.FILE_HASH_CACHE_KEY!,
- {},
- );
+ const hashes = this.getFullFileHashCache();
hashes[filePath] = hash;
await this.context.workspaceState.update(envConfig.FILE_HASH_CACHE_KEY!, hashes);
}
@@ -104,11 +98,19 @@ export class SmellsCacheManager {
* @returns The stored hash or undefined if not found.
*/
public getStoredFileHash(filePath: string): string | undefined {
- const hashes = this.context.workspaceState.get>(
+ const hashes = this.getFullFileHashCache();
+ return hashes[filePath];
+ }
+
+ /**
+ * Retrieves the entire file hash cache.
+ * @returns A record of file paths to SHA256 hashes.
+ */
+ private getFullFileHashCache(): Record {
+ return this.context.workspaceState.get>(
envConfig.FILE_HASH_CACHE_KEY!,
{},
);
- return hashes[filePath];
}
// ============================
From c52f13fb9379f1b7da74b40f5623cadb4bd699ee Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 02:49:12 -0400
Subject: [PATCH 089/215] Removed cache for smells when smells filters was
modified
---
src/extension.ts | 22 ++++++-----
src/providers/FilterViewProvider.ts | 59 +++++++++++++++++++++--------
2 files changed, 56 insertions(+), 25 deletions(-)
diff --git a/src/extension.ts b/src/extension.ts
index da67ee9..364452a 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -27,9 +27,9 @@ export function activate(context: vscode.ExtensionContext): void {
const smellsCacheManager = new SmellsCacheManager(context);
// Initialize the Code Smells View.
- const smellsDisplayProvider = new SmellsViewProvider(context);
+ const smellsViewProvider = new SmellsViewProvider(context);
const codeSmellsView = vscode.window.createTreeView('ecooptimizer.view', {
- treeDataProvider: smellsDisplayProvider,
+ treeDataProvider: smellsViewProvider,
});
context.subscriptions.push(codeSmellsView);
@@ -50,18 +50,22 @@ export function activate(context: vscode.ExtensionContext): void {
// Register workspace-related commands.
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.configureWorkspace', () =>
- configureWorkspace(context, smellsDisplayProvider),
+ configureWorkspace(context, smellsViewProvider),
),
);
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.resetConfiguration', () =>
- resetConfiguration(context, smellsCacheManager, smellsDisplayProvider),
+ resetConfiguration(context, smellsCacheManager, smellsViewProvider),
),
);
// Initialize the Filter Smells View.
- const filterSmellsProvider = new FilterViewProvider(context);
+ const filterSmellsProvider = new FilterViewProvider(
+ context,
+ smellsCacheManager,
+ smellsViewProvider,
+ );
const filterSmellsView = vscode.window.createTreeView('ecooptimizer.filterView', {
treeDataProvider: filterSmellsProvider,
showCollapseAll: true,
@@ -82,13 +86,13 @@ export function activate(context: vscode.ExtensionContext): void {
vscode.commands.registerCommand(
'ecooptimizer.detectSmellsFolder',
(folderPath) =>
- detectSmellsFolder(smellsCacheManager, smellsDisplayProvider, folderPath),
+ detectSmellsFolder(smellsCacheManager, smellsViewProvider, folderPath),
),
);
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.detectSmellsFile', (fileUri) =>
- detectSmellsFile(smellsCacheManager, smellsDisplayProvider, fileUri),
+ detectSmellsFile(smellsCacheManager, smellsViewProvider, fileUri),
),
);
@@ -100,14 +104,14 @@ export function activate(context: vscode.ExtensionContext): void {
// Register the "Clear Smells Cache" command.
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.wipeWorkCache', async () => {
- await wipeWorkCache(smellsCacheManager, smellsDisplayProvider);
+ await wipeWorkCache(smellsCacheManager, smellsViewProvider);
}),
);
// Register the file save listener to detect outdated files.
const fileSaveListener = registerFileSaveListener(
smellsCacheManager,
- smellsDisplayProvider,
+ smellsViewProvider,
);
context.subscriptions.push(fileSaveListener);
}
diff --git a/src/providers/FilterViewProvider.ts b/src/providers/FilterViewProvider.ts
index d8a416a..de9d7c5 100644
--- a/src/providers/FilterViewProvider.ts
+++ b/src/providers/FilterViewProvider.ts
@@ -1,5 +1,7 @@
import * as vscode from 'vscode';
import { FilterSmellConfig, loadSmells, saveSmells } from '../utils/smellsData';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
+import { SmellsViewProvider } from './SmellsViewProvider';
/**
* Provides a tree view for filtering code smells within the VS Code extension.
@@ -14,7 +16,11 @@ export class FilterViewProvider implements vscode.TreeDataProvider;
private smells: Record = {};
- constructor(private context: vscode.ExtensionContext) {
+ constructor(
+ private context: vscode.ExtensionContext,
+ private cacheManager: SmellsCacheManager,
+ private smellsViewProvider: SmellsViewProvider,
+ ) {
this.smells = loadSmells();
}
@@ -50,17 +56,20 @@ export class FilterViewProvider implements vscode.TreeDataProvider {
if (!element) {
return Promise.resolve(
- Object.keys(this.smells).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,
- );
- }),
+ 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,
+ );
+ }),
);
}
@@ -83,30 +92,34 @@ export class FilterViewProvider implements vscode.TreeDataProvider {
if (this.smells[smellKey]) {
this.smells[smellKey].enabled = !this.smells[smellKey].enabled;
saveSmells(this.smells);
+ await this.invalidateCachedSmellsForAffectedFiles();
this._onDidChangeTreeData.fire();
}
}
/**
* Updates the value of a specific smell option and saves the configuration.
+ * Also clears the smell cache for affected files.
* @param smellKey - The key of the smell.
* @param optionKey - The key of the option.
* @param newValue - The new value to set.
*/
- updateOption(
+ async updateOption(
smellKey: string,
optionKey: string,
newValue: number | string,
- ): void {
+ ): Promise {
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(
@@ -124,15 +137,29 @@ export class FilterViewProvider implements vscode.TreeDataProvider {
Object.keys(this.smells).forEach((key) => {
this.smells[key].enabled = enabled;
});
saveSmells(this.smells);
+ await this.invalidateCachedSmellsForAffectedFiles();
this._onDidChangeTreeData.fire();
}
+
+ /**
+ * Clears smell cache and marks all cached file results as outdated.
+ */
+ async invalidateCachedSmellsForAffectedFiles(): Promise {
+ const cache = this.cacheManager.getFullSmellCache();
+
+ for (const [filePath, smells] of Object.entries(cache)) {
+ await this.cacheManager.clearCachedSmellsForFile(filePath);
+ this.smellsViewProvider.markFileAsOutdated(filePath);
+ }
+ }
}
/**
From 300b5838208eeb5be14cf8b26254b934a01e7eb5 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 02:59:51 -0400
Subject: [PATCH 090/215] added warning pop-up if users changed filter
---
src/providers/FilterViewProvider.ts | 31 ++++++++++++++++++++++++-----
1 file changed, 26 insertions(+), 5 deletions(-)
diff --git a/src/providers/FilterViewProvider.ts b/src/providers/FilterViewProvider.ts
index de9d7c5..b1e5944 100644
--- a/src/providers/FilterViewProvider.ts
+++ b/src/providers/FilterViewProvider.ts
@@ -31,12 +31,18 @@ export class FilterViewProvider implements vscode.TreeDataProvider): void {
this.treeView = treeView;
- this.treeView.onDidChangeCheckboxState((event) => {
- event.items.forEach((item) => {
- if (item[0] instanceof SmellItem) {
- this.toggleSmell(item[0].key);
+ 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 {
+ // Cancelled — refresh the tree to revert the checkbox UI
+ this._onDidChangeTreeData.fire();
+ }
}
- });
+ }
});
}
@@ -116,6 +122,9 @@ export class FilterViewProvider implements vscode.TreeDataProvider {
+ 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);
@@ -141,6 +150,9 @@ export class FilterViewProvider implements vscode.TreeDataProvider {
+ const confirmed = await this.confirmFilterChange();
+ if (!confirmed) return;
+
Object.keys(this.smells).forEach((key) => {
this.smells[key].enabled = enabled;
});
@@ -160,6 +172,15 @@ export class FilterViewProvider implements vscode.TreeDataProvider {
+ const result = await vscode.window.showWarningMessage(
+ 'Changing smell filters will invalidate existing analysis results. Do you want to continue?',
+ { modal: true },
+ 'Yes',
+ );
+ return result === 'Yes';
+ }
}
/**
From 6521b104c383e762dd973c9452917b1ed9cbc734 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 03:01:17 -0400
Subject: [PATCH 091/215] comment
---
src/providers/FilterViewProvider.ts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/providers/FilterViewProvider.ts b/src/providers/FilterViewProvider.ts
index b1e5944..5c16c80 100644
--- a/src/providers/FilterViewProvider.ts
+++ b/src/providers/FilterViewProvider.ts
@@ -173,6 +173,13 @@ export class FilterViewProvider implements vscode.TreeDataProvider {
const result = await vscode.window.showWarningMessage(
'Changing smell filters will invalidate existing analysis results. Do you want to continue?',
From 878d5cab44c5dd5558dd47b72216ae49ccb6efc5 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 14:38:22 -0400
Subject: [PATCH 092/215] fixed when refactor smell by type icon is shown
---
package.json | 9 ++-------
src/managers/SmellsViewUIManager.ts | 5 +++++
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/package.json b/package.json
index 56110de..bd1fd41 100644
--- a/package.json
+++ b/package.json
@@ -196,11 +196,6 @@
"icon": "$(search)",
"category": "Eco Optimizer"
},
- {
- "command": "ecooptimizer.refactorSmell",
- "title": "Refactor Smell",
- "category": "Eco"
- },
{
"command": "ecooptimizer.refactorAllSmellsOfType",
"title": "Refactor Smells By Type",
@@ -254,12 +249,12 @@
},
{
"command": "ecooptimizer.detectSmellsFile",
- "when": "viewItem == ecoOptimizerFile",
+ "when": "viewItem == ecoOptimizerFile || viewItem == ecoOptimizerFile-hasSmells",
"group": "inline"
},
{
"command": "ecooptimizer.refactorAllSmellsOfType",
- "when": "viewItem == ecoOptimizerFile",
+ "when": "viewItem == ecoOptimizerFile-hasSmells",
"group": "inline"
}
]
diff --git a/src/managers/SmellsViewUIManager.ts b/src/managers/SmellsViewUIManager.ts
index d164dca..e3f6651 100644
--- a/src/managers/SmellsViewUIManager.ts
+++ b/src/managers/SmellsViewUIManager.ts
@@ -46,6 +46,11 @@ export class SmellsUIManager {
item.contextValue = 'ecoOptimizerFile';
this.assignOpenFileCommand(item, element);
this.updateFileItem(item, status, isOutdated);
+
+ // Add a context value for files with smells
+ if (hasSmells && status === 'passed') {
+ item.contextValue = 'ecoOptimizerFile-hasSmells'; // Append 'hasSmells' to the context value
+ }
} else {
item.contextValue = 'ecoOptimizerSmell';
const parentFile = this.stateManager.getFileForSmell(element);
From e574404cf3ae9a4c2eba36004f9a147bfd23f8ac Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 14:53:18 -0400
Subject: [PATCH 093/215] added fix for outdated files showing refactor button
---
package.json | 11 +++++++++++
src/managers/SmellsViewUIManager.ts | 10 +++++-----
2 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/package.json b/package.json
index bd1fd41..cdbd86d 100644
--- a/package.json
+++ b/package.json
@@ -202,6 +202,12 @@
"icon": "$(tools)",
"category": "Eco Optimizer"
},
+ {
+ "command": "ecooptimizer.refactorSmell",
+ "title": "Refactor Smell",
+ "icon": "$(tools)",
+ "category": "Eco Optimizer"
+ },
{
"command": "ecooptimizer.jumpToSmell",
"title": "Jump to Smell in File",
@@ -256,6 +262,11 @@
"command": "ecooptimizer.refactorAllSmellsOfType",
"when": "viewItem == ecoOptimizerFile-hasSmells",
"group": "inline"
+ },
+ {
+ "command": "ecooptimizer.refactorSmell",
+ "when": "viewItem == ecoOptimizerSmell",
+ "group": "inline"
}
]
}
diff --git a/src/managers/SmellsViewUIManager.ts b/src/managers/SmellsViewUIManager.ts
index e3f6651..0f002af 100644
--- a/src/managers/SmellsViewUIManager.ts
+++ b/src/managers/SmellsViewUIManager.ts
@@ -48,7 +48,7 @@ export class SmellsUIManager {
this.updateFileItem(item, status, isOutdated);
// Add a context value for files with smells
- if (hasSmells && status === 'passed') {
+ if (hasSmells && status === 'passed' && !isOutdated) {
item.contextValue = 'ecoOptimizerFile-hasSmells'; // Append 'hasSmells' to the context value
}
} else {
@@ -62,7 +62,6 @@ export class SmellsUIManager {
}
this.setSmellTooltip(item, element);
}
-
return item;
}
@@ -96,12 +95,13 @@ export class SmellsUIManager {
'warning',
new vscode.ThemeColor('charts.orange'),
);
+ item.tooltip = `${path.basename(this.getStatusMessage('outdated'))}`;
} else {
item.iconPath = this.getStatusIcon(status);
+ item.tooltip = `${path.basename(
+ item.label as string,
+ )} (${this.getStatusMessage(status)})`;
}
- item.tooltip = `${path.basename(
- item.label as string,
- )} (${this.getStatusMessage(status)})`;
}
/**
From c69d477230606a9969f5b612ed7e5b292d44b640 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 17:46:28 -0400
Subject: [PATCH 094/215] minor change how we process smells in smells view
---
src/commands/detectSmells.ts | 4 +--
src/managers/SmellsViewStateManager.ts | 42 +++++---------------------
src/providers/SmellsViewProvider.ts | 11 ++-----
3 files changed, 12 insertions(+), 45 deletions(-)
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index edde8e6..71087e9 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -59,7 +59,7 @@ export async function detectSmellsFile(
);
if (cachedSmells.length > 0) {
- treeDataProvider.updateSmells(filePath, cachedSmells, enabledSmells);
+ treeDataProvider.updateSmells(filePath, cachedSmells);
} else {
treeDataProvider.updateStatus(filePath, 'no_issues');
}
@@ -96,7 +96,7 @@ export async function detectSmellsFile(
treeDataProvider.clearOutdatedStatus(filePath);
if (smells.length > 0) {
- treeDataProvider.updateSmells(filePath, smells, enabledSmells);
+ treeDataProvider.updateSmells(filePath, smells);
vscode.window.showInformationMessage(
`Analysis complete: Detected ${
smells.length
diff --git a/src/managers/SmellsViewStateManager.ts b/src/managers/SmellsViewStateManager.ts
index 136751c..8f2c231 100644
--- a/src/managers/SmellsViewStateManager.ts
+++ b/src/managers/SmellsViewStateManager.ts
@@ -1,19 +1,8 @@
import * as path from 'path';
-interface DetectedSmell {
- messageId: string;
- symbol: string;
- occurences: { line: number; endLine?: number }[];
-}
-
-interface ProcessedSmell {
- acronym: string;
- occurrences: { line: number; endLine?: number }[];
-}
-
export class SmellsStateManager {
private fileStatusMap: Map = new Map();
- private detectedSmells: Map = new Map();
+ private detectedSmells: Map = new Map(); // Use Smell[] instead of ProcessedSmell[]
private smellToFileMap: Map = new Map();
private modifiedFiles: Map = new Map();
@@ -21,36 +10,19 @@ export class SmellsStateManager {
* Updates the detected smells for a file.
* @param filePath - The analyzed file path.
* @param smells - The detected smells in the file.
- * @param smellMetadata - Metadata containing message ID and acronym for each smell.
*/
- updateSmells(
- filePath: string,
- smells: DetectedSmell[],
- smellMetadata: Record,
- ): void {
+ updateSmells(filePath: string, smells: Smell[]): void {
this.fileStatusMap.set(filePath, 'passed');
- const formattedSmells: ProcessedSmell[] = smells.map((smell) => {
- const foundEntry = Object.values(smellMetadata).find(
- (smellData) => smellData.message_id === smell.messageId,
- ) as { message_id: string; acronym: string };
-
- return {
- acronym: foundEntry ? foundEntry.acronym : smell.messageId,
- occurrences: smell.occurences.map((occ) => ({
- line: occ.line,
- endLine: occ.endLine,
- })),
- };
- });
-
- this.detectedSmells.set(filePath, formattedSmells);
+ // Update the detected smells for the file
+ this.detectedSmells.set(filePath, smells);
+ // Update the detected smells for the folder
const folderPath = path.dirname(filePath);
if (!this.detectedSmells.has(folderPath)) {
this.detectedSmells.set(folderPath, []);
}
- this.detectedSmells.get(folderPath)?.push(...formattedSmells);
+ this.detectedSmells.get(folderPath)?.push(...smells);
}
/**
@@ -110,7 +82,7 @@ export class SmellsStateManager {
* @param filePath - The path of the file.
* @returns An array of smell entries.
*/
- getSmellsForFile(filePath: string): ProcessedSmell[] {
+ getSmellsForFile(filePath: string): Smell[] {
return this.detectedSmells.get(filePath) || [];
}
diff --git a/src/providers/SmellsViewProvider.ts b/src/providers/SmellsViewProvider.ts
index 866ef36..5be0e1d 100644
--- a/src/providers/SmellsViewProvider.ts
+++ b/src/providers/SmellsViewProvider.ts
@@ -60,7 +60,7 @@ export class SmellsViewProvider implements vscode.TreeDataProvider {
// If the file is not outdated, return the detected smells
const smells = this.stateManager.getSmellsForFile(element);
return smells.map((smell) => {
- const smellItem = `${smell.acronym}: Line ${smell.occurrences
+ const smellItem = `${smell.messageId}: Line ${smell.occurences
.map((o) => o.line)
.join(', ')}`;
this.stateManager.mapSmellToFile(smellItem, element);
@@ -72,14 +72,9 @@ export class SmellsViewProvider implements vscode.TreeDataProvider {
* Updates the detected smells for a file and refreshes the tree view.
* @param filePath - The analyzed file path.
* @param smells - The detected smells in the file.
- * @param smellMetadata - Metadata containing message ID and acronym for each smell.
*/
- updateSmells(
- filePath: string,
- smells: Smell[],
- smellMetadata: Record,
- ): void {
- this.stateManager.updateSmells(filePath, smells, smellMetadata);
+ updateSmells(filePath: string, smells: Smell[]): void {
+ this.stateManager.updateSmells(filePath, smells);
this.refresh();
}
From 31b00f60cffd9f2752f80ad1ca1f79c20ddfd4c9 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 22:43:51 -0400
Subject: [PATCH 095/215] made smells have a unique identifier to help make
refactoring
---
src/api/backend.ts | 72 +++++++++++++++--
src/commands/detectSmells.ts | 3 +-
src/context/SmellsCacheManager.ts | 98 +++++++++++++++++------
src/extension.ts | 35 +++++++++
src/global.d.ts | 17 ++++
src/managers/SmellsViewUIManager.ts | 116 ++++++++++++++++++----------
src/providers/SmellsViewProvider.ts | 2 +-
7 files changed, 273 insertions(+), 70 deletions(-)
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 544ddea..64d8ee5 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -1,13 +1,16 @@
-
import { envConfig } from '../utils/envConfig';
import { serverStatus } from '../utils/serverStatus';
import { ServerStatusType } from '../utils/serverStatus';
+import * as vscode from 'vscode';
const BASE_URL = `http://${envConfig.SERVER_URL}`; // API URL for Python backend
+/**
+ * Checks the status of the backend server.
+ */
export async function checkServerStatus(): Promise {
try {
- const response = await fetch('http://localhost:8000/health');
+ const response = await fetch(`${BASE_URL}/health`);
if (response.ok) {
serverStatus.setStatus(ServerStatusType.UP);
} else {
@@ -20,7 +23,7 @@ export async function checkServerStatus(): Promise {
/**
* Sends a request to the backend to detect code smells in the specified file.
- *
+ *
* @param filePath - The absolute path to the file being analyzed.
* @param enabledSmells - A dictionary containing enabled smells and their configured options.
* @returns A promise resolving to the backend response or throwing an error if unsuccessful.
@@ -33,9 +36,9 @@ export async function fetchSmells(
try {
const response = await fetch(url, {
- method: "POST",
+ method: 'POST',
headers: {
- "Content-Type": "application/json",
+ 'Content-Type': 'application/json',
},
body: JSON.stringify({
file_path: filePath,
@@ -52,7 +55,7 @@ export async function fetchSmells(
const smellsList = await response.json();
if (!Array.isArray(smellsList)) {
- throw new Error("Unexpected response format from backend.");
+ throw new Error('Unexpected response format from backend.');
}
return { smells: smellsList, status: response.status };
@@ -61,4 +64,61 @@ export async function fetchSmells(
`Failed to connect to the backend: ${error.message}. Please check your network and try again.`
);
}
+}
+
+/**
+ * Sends a request to the backend to refactor a specific smell.
+ *
+ * @param filePath - The absolute path to the file containing the smell.
+ * @param smell - The smell to refactor.
+ * @returns A promise resolving to the refactored data or throwing an error if unsuccessful.
+ */
+export async function refactorSmell(
+ filePath: string,
+ smell: Smell
+): Promise {
+ const url = `${BASE_URL}/refactor`;
+
+ const workspaceFolder = vscode.workspace.workspaceFolders?.find((folder) =>
+ filePath.includes(folder.uri.fsPath)
+ );
+
+ 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 "${workspaceFolderPath}"`
+ );
+
+ const payload = {
+ source_dir: workspaceFolderPath,
+ smell,
+ };
+
+ try {
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(payload),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(errorData.detail || 'Refactoring failed');
+ }
+
+ const refactorResult = (await response.json()) as RefactoredData;
+ return refactorResult;
+ } catch (error: any) {
+ console.error('Eco: Unexpected error in refactorSmell:', error);
+ throw new Error(`Refactoring failed: ${error.message}`);
+ }
}
\ No newline at end of file
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index 71087e9..bee79e8 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -91,12 +91,13 @@ export async function detectSmellsFile(
if (status === 200) {
// Cache detected smells, even if no smells are found
await smellsCacheManager.setCachedSmells(filePath, smells);
+ const smellsWithID = smellsCacheManager.getCachedSmells(filePath) || [];
// Remove the file from modifiedFiles after re-analysis
treeDataProvider.clearOutdatedStatus(filePath);
if (smells.length > 0) {
- treeDataProvider.updateSmells(filePath, smells);
+ treeDataProvider.updateSmells(filePath, smellsWithID);
vscode.window.showInformationMessage(
`Analysis complete: Detected ${
smells.length
diff --git a/src/context/SmellsCacheManager.ts b/src/context/SmellsCacheManager.ts
index f1239e8..a73e132 100644
--- a/src/context/SmellsCacheManager.ts
+++ b/src/context/SmellsCacheManager.ts
@@ -5,6 +5,8 @@ import { envConfig } from '../utils/envConfig';
/**
* Manages caching of detected smells and file hashes to avoid redundant backend calls.
+ * This class handles storing, retrieving, and clearing cached smells and file hashes,
+ * as well as refreshing the UI when the cache is updated.
*/
export class SmellsCacheManager {
constructor(private context: vscode.ExtensionContext) {}
@@ -13,33 +15,78 @@ export class SmellsCacheManager {
// Smell Caching Methods
// ============================
+ /**
+ * Generates a unique string ID for a smell based on its content.
+ * The ID is derived from a SHA256 hash of the smell object.
+ *
+ * @param smell - The smell object to generate an ID for.
+ * @returns A unique string ID for the smell.
+ */
+ private generateSmellId(smell: Smell): string {
+ // Generate a SHA256 hash of the smell object
+ const smellHash = createHash('sha256')
+ .update(JSON.stringify(smell))
+ .digest('hex');
+
+ // Use the first 8 characters of the hash as the ID
+ return smellHash.substring(0, 3);
+ }
+
+ /**
+ * Caches detected smells for a given file and assigns unique string IDs to each smell.
+ * The smells are stored in the workspace state for persistence.
+ *
+ * @param filePath - The absolute path of the file.
+ * @param smells - The detected smells to store.
+ */
+ public async setCachedSmells(filePath: string, smells: Smell[]): Promise {
+ const cache = this.getFullSmellCache();
+
+ // Assign unique string IDs to each smell
+ const smellsWithIds = smells.map((smell) => {
+ const id = this.generateSmellId(smell);
+ return {
+ ...smell,
+ id, // Add the unique string ID to the smell object
+ };
+ });
+
+ // Update the cache with the new smells
+ cache[filePath] = smellsWithIds;
+ await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
+ }
+
/**
* Retrieves cached smells for a given file.
- * If the file has been analyzed and found clean, this will return an empty array.
*
* @param filePath - The absolute path of the file.
- * @returns An array of detected smells or `undefined` if the file has not been analyzed.
+ * @returns An array of detected smells with unique IDs, or undefined if no smells are cached.
*/
public getCachedSmells(filePath: string): Smell[] | undefined {
const cache = this.getFullSmellCache();
- return cache[filePath]; // May be undefined
+ return cache[filePath];
}
/**
- * Caches detected smells for a given file.
- * If no smells are detected, caches an empty array to avoid redundant backend calls.
+ * Retrieves a smell by its unique string ID.
*
- * @param filePath - The absolute path of the file.
- * @param smells - The detected smells to store (empty array if no smells found).
+ * @param id - The unique string ID of the smell.
+ * @returns The smell object, or undefined if no smell matches the ID.
*/
- public async setCachedSmells(filePath: string, smells: Smell[]): Promise {
+ public getSmellById(id: string): Smell | undefined {
const cache = this.getFullSmellCache();
- cache[filePath] = smells;
- await this.context.workspaceState.update(envConfig.SMELL_CACHE_KEY!, cache);
+ for (const filePath in cache) {
+ const smells = cache[filePath];
+ const smell = smells.find((s) => s.id === id);
+ if (smell) {
+ return smell;
+ }
+ }
+ return undefined;
}
/**
- * Clears all cached smells from the workspace.
+ * Clears all cached smells from the workspace state.
* This forces a fresh analysis of all files when `detectSmellsFile` is called.
*/
public async clearSmellsCache(): Promise {
@@ -58,8 +105,9 @@ export class SmellsCacheManager {
}
/**
- * Retrieves the entire smell cache.
- * @returns A record of file paths to cached smells.
+ * Retrieves the entire smell cache from the workspace state.
+ *
+ * @returns A record of file paths to their corresponding cached smells.
*/
public getFullSmellCache(): Record {
return this.context.workspaceState.get>(
@@ -73,18 +121,20 @@ export class SmellsCacheManager {
// ============================
/**
- * Computes a SHA256 hash of a file's contents.
+ * Computes a SHA256 hash of a file's contents and returns it as a string.
+ *
* @param content - The file content as a string.
- * @returns A SHA256 hash string.
+ * @returns A SHA256 hash string derived from the file content.
*/
public computeFileHash(content: string): string {
return createHash('sha256').update(content).digest('hex');
}
/**
- * Stores a hash of a file's contents in workspaceState.
- * @param filePath - Absolute path of the file.
- * @param hash - The computed file hash.
+ * Stores a hash of a file's contents in the workspace state.
+ *
+ * @param filePath - The absolute path of the file.
+ * @param hash - The computed file hash as a string.
*/
public async storeFileHash(filePath: string, hash: string): Promise {
const hashes = this.getFullFileHashCache();
@@ -94,8 +144,9 @@ export class SmellsCacheManager {
/**
* Retrieves the stored hash for a given file.
- * @param filePath - Absolute path of the file.
- * @returns The stored hash or undefined if not found.
+ *
+ * @param filePath - The absolute path of the file.
+ * @returns The stored hash as a string, or undefined if no hash is found.
*/
public getStoredFileHash(filePath: string): string | undefined {
const hashes = this.getFullFileHashCache();
@@ -103,8 +154,9 @@ export class SmellsCacheManager {
}
/**
- * Retrieves the entire file hash cache.
- * @returns A record of file paths to SHA256 hashes.
+ * Retrieves the entire file hash cache from the workspace state.
+ *
+ * @returns A record of file paths to their corresponding SHA256 hashes.
*/
private getFullFileHashCache(): Record {
return this.context.workspaceState.get>(
@@ -119,7 +171,7 @@ export class SmellsCacheManager {
/**
* Clears all cached smells and refreshes the UI.
- * Used by both "Clear Smells Cache" and "Reset Configuration".
+ * This method is used by both "Clear Smells Cache" and "Reset Configuration" commands.
*
* @param smellsDisplayProvider - The tree view provider responsible for the UI.
*/
diff --git a/src/extension.ts b/src/extension.ts
index 364452a..653a125 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -96,6 +96,41 @@ export function activate(context: vscode.ExtensionContext): void {
),
);
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.refactorSmell', (fileUri) => {
+ // Ensure the fileUri is valid
+ if (!fileUri) {
+ console.error('No file URI provided.');
+ return;
+ }
+
+ // Extract the smell ID from the fileUri string (e.g., "(aa7) R0913: Line 15")
+ const smellIdMatch = fileUri.match(/\(([^)]+)\)/);
+ const smellId = smellIdMatch ? smellIdMatch[1] : null;
+
+ if (!smellId) {
+ console.error('No smell ID found in the file URI:', fileUri);
+ return;
+ }
+
+ // Retrieve the smell object by ID using the cache manager
+ const smell = smellsCacheManager.getSmellById(smellId);
+ if (!smell) {
+ console.error('No smell found with ID:', smellId);
+ return;
+ }
+
+ // Get the file path from the smell object
+ const filePath = smell.path;
+
+ // Print the file path and smell object to the console
+ console.log('File Path:', filePath);
+ console.log('Smell Object:', smell);
+
+ // Add additional logic here to handle refactoring
+ }),
+ );
+
// Register the "Jump to Smell" command.
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.jumpToSmell', jumpToSmell),
diff --git a/src/global.d.ts b/src/global.d.ts
index ae62978..8ae153e 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -32,5 +32,22 @@ declare global {
obj?: string; // Optional: Object name associated with the smell (if applicable)
occurences: Occurrence[]; // Optional: List of occurrences for repeated calls
additionalInfo: AdditionalInfo;
+ id?: string; // Add this line to include the unique ID
+ }
+
+ /**
+ * Represents the response from the `/refactor` endpoint.
+ */
+ export interface RefactoredData {
+ tempDir: string;
+ targetFile: {
+ original: string;
+ refactored: string;
+ };
+ energySaved: number | null;
+ affectedFiles: {
+ original: string;
+ refactored: string;
+ }[];
}
}
diff --git a/src/managers/SmellsViewUIManager.ts b/src/managers/SmellsViewUIManager.ts
index 0f002af..15623a4 100644
--- a/src/managers/SmellsViewUIManager.ts
+++ b/src/managers/SmellsViewUIManager.ts
@@ -1,72 +1,103 @@
import * as vscode from 'vscode';
-import * as path from 'path';
import * as fs from 'fs';
+import * as path from 'path';
import { SmellsStateManager } from './SmellsViewStateManager';
+/**
+ * Manages the UI representation of files, folders, and detected smells in the VS Code tree view.
+ * This class handles creating tree items, assigning commands, and updating item states based on
+ * the analysis status and file state (e.g., outdated, queued, passed, failed).
+ */
export class SmellsUIManager {
constructor(private stateManager: SmellsStateManager) {}
/**
* Creates a tree item for a given element (folder, file, or smell).
+ * The tree item's appearance and behavior depend on the type of element and its current state.
+ *
* @param element - The file or folder path, or a detected smell.
+ * @returns A `vscode.TreeItem` representing the element.
*/
createTreeItem(element: string): vscode.TreeItem {
+ // Retrieve the current status and smell information for the element
const status = this.stateManager.getFileStatus(element);
const hasSmells = this.stateManager.getSmellsForFile(element).length > 0;
const isDirectory = fs.existsSync(element) && fs.statSync(element).isDirectory();
const isSmellItem = !fs.existsSync(element) && !isDirectory;
- // Check if the file is outdated
+ // Check if the file is outdated (needs reanalysis)
const isOutdated =
!isDirectory && !isSmellItem && this.stateManager.isFileOutdated(element);
- // Set the collapsible state
+ // Determine the collapsible state of the tree item
let collapsibleState: vscode.TreeItemCollapsibleState;
if (isDirectory) {
- // Directories are always collapsible
- collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
+ collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; // Folders are collapsible
} else if (isSmellItem) {
- // Smell items are never collapsible
- collapsibleState = vscode.TreeItemCollapsibleState.None;
+ collapsibleState = vscode.TreeItemCollapsibleState.None; // Smells are not collapsible
} else if (isOutdated) {
- // Outdated files are not collapsible
- collapsibleState = vscode.TreeItemCollapsibleState.None;
+ collapsibleState = vscode.TreeItemCollapsibleState.None; // Outdated files are not collapsible
} else {
- // Files with smells are collapsible
collapsibleState = hasSmells
- ? vscode.TreeItemCollapsibleState.Collapsed
- : vscode.TreeItemCollapsibleState.None;
+ ? vscode.TreeItemCollapsibleState.Collapsed // Files with smells are collapsible
+ : vscode.TreeItemCollapsibleState.None; // Files without smells are not collapsible
}
+ // Create the tree item with the element's basename and collapsible state
const item = new vscode.TreeItem(path.basename(element), collapsibleState);
+ // Customize the tree item based on its type (folder, file, or smell)
if (isDirectory) {
+ // Folders have a specific context value for styling and behavior
item.contextValue = 'ecoOptimizerFolder';
} else if (!isSmellItem) {
+ // Files have a specific context value and can be opened
item.contextValue = 'ecoOptimizerFile';
- this.assignOpenFileCommand(item, element);
- this.updateFileItem(item, status, isOutdated);
+ this.assignOpenFileCommand(item, element); // Assign a command to open the file
+ this.updateFileItem(item, status, isOutdated); // Update the item's appearance based on status
- // Add a context value for files with smells
+ // Add a context value for files with detected smells
if (hasSmells && status === 'passed' && !isOutdated) {
- item.contextValue = 'ecoOptimizerFile-hasSmells'; // Append 'hasSmells' to the context value
+ item.contextValue = 'ecoOptimizerFile-hasSmells';
}
} else {
+ // Smells have a specific context value and display detailed information
item.contextValue = 'ecoOptimizerSmell';
+
+ // Retrieve the parent file and smell object for the smell item
const parentFile = this.stateManager.getFileForSmell(element);
if (parentFile) {
- const [, lineStr] = element.split(': Line ');
- const lines = lineStr.split(',').map((line) => parseInt(line.trim(), 10));
- const firstLine = lines.length > 0 ? lines[0] - 1 : 0;
- this.assignJumpToSmellCommand(item, parentFile, firstLine);
+ const smells = this.stateManager.getSmellsForFile(parentFile);
+
+ // Extract the smell ID from the element's label
+ const idMatch = element.match(/\(([^)]+)\)/);
+ const id = idMatch ? idMatch[1] : null;
+
+ // Find the smell by its ID
+ const smell = smells.find((s) => s.id === id);
+
+ if (smell) {
+ // Set the label and description for the smell item
+ item.label = `${smell.messageId}: Line ${smell.occurences
+ .map((o) => o.line)
+ .join(', ')} (ID: ${smell.id}) `;
+
+ // Assign a command to jump to the first occurrence of the smell in the file
+ const firstLine = smell.occurences[0]?.line - 1 || 0; // Default to line 0 if no occurrences
+ this.assignJumpToSmellCommand(item, parentFile, firstLine);
+ }
}
+
+ // Set the tooltip for the smell item
this.setSmellTooltip(item, element);
}
+
return item;
}
/**
* Assigns a command to open a file when the tree item is clicked.
+ *
* @param item - The tree item to update.
* @param filePath - The path of the file to open.
*/
@@ -79,7 +110,27 @@ export class SmellsUIManager {
}
/**
- * Updates the file item's status, including icon, message, and description.
+ * Assigns a command to jump to a specific line in a file when the tree item is clicked.
+ *
+ * @param item - The tree item to update.
+ * @param filePath - The path of the file containing the smell.
+ * @param line - The line number to jump to.
+ */
+ private assignJumpToSmellCommand(
+ item: vscode.TreeItem,
+ filePath: string,
+ line: number,
+ ): void {
+ item.command = {
+ command: 'ecooptimizer.jumpToSmell',
+ title: 'Jump to Smell',
+ arguments: [filePath, line],
+ };
+ }
+
+ /**
+ * Updates the file item's appearance based on its analysis status and whether it is outdated.
+ *
* @param item - The tree item to update.
* @param status - The analysis status (e.g., "queued", "passed", "failed", "outdated").
* @param isOutdated - Whether the file is outdated.
@@ -90,6 +141,7 @@ export class SmellsUIManager {
isOutdated: boolean,
): void {
if (isOutdated) {
+ // Mark the file as outdated with a warning icon and description
item.description = 'outdated';
item.iconPath = new vscode.ThemeIcon(
'warning',
@@ -97,6 +149,7 @@ export class SmellsUIManager {
);
item.tooltip = `${path.basename(this.getStatusMessage('outdated'))}`;
} else {
+ // Set the icon and tooltip based on the analysis status
item.iconPath = this.getStatusIcon(status);
item.tooltip = `${path.basename(
item.label as string,
@@ -104,26 +157,9 @@ export class SmellsUIManager {
}
}
- /**
- * Assigns a command to jump to a specific line in a file when the tree item is clicked.
- * @param item - The tree item to update.
- * @param filePath - The path of the file containing the smell.
- * @param line - The line number to jump to.
- */
- private assignJumpToSmellCommand(
- item: vscode.TreeItem,
- filePath: string,
- line: number,
- ): void {
- item.command = {
- command: 'ecooptimizer.jumpToSmell',
- title: 'Jump to Smell',
- arguments: [filePath, line],
- };
- }
-
/**
* Sets the tooltip for a smell item.
+ *
* @param item - The tree item to update.
* @param smellDescription - The description of the smell.
*/
@@ -133,6 +169,7 @@ export class SmellsUIManager {
/**
* Retrieves the appropriate VS Code icon based on the smell analysis status.
+ *
* @param status - The analysis status.
* @returns The corresponding VS Code theme icon.
*/
@@ -166,6 +203,7 @@ export class SmellsUIManager {
/**
* Retrieves the status message corresponding to the smell analysis state.
+ *
* @param status - The analysis status.
* @returns A descriptive status message.
*/
diff --git a/src/providers/SmellsViewProvider.ts b/src/providers/SmellsViewProvider.ts
index 5be0e1d..f61082e 100644
--- a/src/providers/SmellsViewProvider.ts
+++ b/src/providers/SmellsViewProvider.ts
@@ -62,7 +62,7 @@ export class SmellsViewProvider implements vscode.TreeDataProvider {
return smells.map((smell) => {
const smellItem = `${smell.messageId}: Line ${smell.occurences
.map((o) => o.line)
- .join(', ')}`;
+ .join(', ')} (ID: ${smell.id})`;
this.stateManager.mapSmellToFile(smellItem, element);
return smellItem;
});
From 26c6a9fcdb912400b59f12dec1d68d9f2c8a6d4c Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Fri, 21 Mar 2025 22:58:49 -0400
Subject: [PATCH 096/215] Added error catching for smell detection commands +
fixed hashing logic
---
src/commands/detectSmells.ts | 14 +++++++-------
src/context/SmellsCacheManager.ts | 6 +++---
src/extension.ts | 19 ++++++++++++++-----
src/providers/SmellsViewProvider.ts | 1 +
4 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index edde8e6..575c9d0 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -32,13 +32,6 @@ export async function detectSmellsFile(
// Handle outdated files before proceeding
await handleOutdatedFile(filePath, smellsCacheManager, treeDataProvider);
- // Open the file and compute its hash
- const document = await vscode.workspace.openTextDocument(filePath);
- const fileContent = document.getText();
-
- // Store the file hash after analyzing
- await smellsCacheManager.storeFileHash(filePath, fileContent);
-
// Retrieve enabled smells from configuration
const enabledSmells = getEnabledSmells();
@@ -52,6 +45,7 @@ export async function detectSmellsFile(
// Check if smells are already cached
const cachedSmells = smellsCacheManager.getCachedSmells(filePath);
+ console.log('Cached smells:', cachedSmells);
if (cachedSmells !== undefined) {
// Use cached smells if available
vscode.window.showInformationMessage(
@@ -188,6 +182,12 @@ async function handleOutdatedFile(
// Delete cached smells for the outdated file
await smellsCacheManager.clearCachedSmellsForFile(filePath);
+ const document = await vscode.workspace.openTextDocument(filePath);
+ const fileContent = document.getText();
+
+ console.log('Storing file hash for:', filePath);
+ await smellsCacheManager.storeFileHash(filePath, fileContent);
+
// Remove the outdated status from the UI
smellsDisplayProvider.updateStatus(filePath, 'queued'); // Reset to "queued" or another default status
}
diff --git a/src/context/SmellsCacheManager.ts b/src/context/SmellsCacheManager.ts
index f1239e8..5e734f4 100644
--- a/src/context/SmellsCacheManager.ts
+++ b/src/context/SmellsCacheManager.ts
@@ -84,11 +84,11 @@ export class SmellsCacheManager {
/**
* Stores a hash of a file's contents in workspaceState.
* @param filePath - Absolute path of the file.
- * @param hash - The computed file hash.
+ * @param content - The file content to hash.
*/
- public async storeFileHash(filePath: string, hash: string): Promise {
+ public async storeFileHash(filePath: string, content: string): Promise {
const hashes = this.getFullFileHashCache();
- hashes[filePath] = hash;
+ hashes[filePath] = this.computeFileHash(content);
await this.context.workspaceState.update(envConfig.FILE_HASH_CACHE_KEY!, hashes);
}
diff --git a/src/extension.ts b/src/extension.ts
index 364452a..448ee14 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -85,15 +85,24 @@ export function activate(context: vscode.ExtensionContext): void {
context.subscriptions.push(
vscode.commands.registerCommand(
'ecooptimizer.detectSmellsFolder',
- (folderPath) =>
- detectSmellsFolder(smellsCacheManager, smellsViewProvider, folderPath),
+ (folderPath) => {
+ try {
+ detectSmellsFolder(smellsCacheManager, smellsViewProvider, folderPath);
+ } catch (error: any) {
+ vscode.window.showErrorMessage(`Error detecting smells: ${error.message}`);
+ }
+ },
),
);
context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.detectSmellsFile', (fileUri) =>
- detectSmellsFile(smellsCacheManager, smellsViewProvider, fileUri),
- ),
+ vscode.commands.registerCommand('ecooptimizer.detectSmellsFile', (fileUri) => {
+ try {
+ detectSmellsFile(smellsCacheManager, smellsViewProvider, fileUri);
+ } catch (error: any) {
+ vscode.window.showErrorMessage(`Error detecting smells: ${error.message}`);
+ }
+ }),
);
// Register the "Jump to Smell" command.
diff --git a/src/providers/SmellsViewProvider.ts b/src/providers/SmellsViewProvider.ts
index 866ef36..ac9a30e 100644
--- a/src/providers/SmellsViewProvider.ts
+++ b/src/providers/SmellsViewProvider.ts
@@ -79,6 +79,7 @@ export class SmellsViewProvider implements vscode.TreeDataProvider {
smells: Smell[],
smellMetadata: Record,
): void {
+ console.log('Updating UI with detected smells...', smells);
this.stateManager.updateSmells(filePath, smells, smellMetadata);
this.refresh();
}
From 4fcd83e19d8e1f8e3fb6b768cce0dfc3d45eb651 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 23:15:05 -0400
Subject: [PATCH 097/215] fix bug when jumping to smell
---
src/managers/SmellsViewUIManager.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/managers/SmellsViewUIManager.ts b/src/managers/SmellsViewUIManager.ts
index 15623a4..1899d7e 100644
--- a/src/managers/SmellsViewUIManager.ts
+++ b/src/managers/SmellsViewUIManager.ts
@@ -70,7 +70,7 @@ export class SmellsUIManager {
const smells = this.stateManager.getSmellsForFile(parentFile);
// Extract the smell ID from the element's label
- const idMatch = element.match(/\(([^)]+)\)/);
+ const idMatch = element.match(/\(ID:\s*([^)]+)\)/);
const id = idMatch ? idMatch[1] : null;
// Find the smell by its ID
From 437b9eb69577bbf6ea9ea646eaa67f11eabb0a1c Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Fri, 21 Mar 2025 23:31:22 -0400
Subject: [PATCH 098/215] added inital refactoring stage
---
src/commands/refactorSmell.ts | 41 ++++++++++++++++++++++++++---------
src/extension.ts | 7 ++++--
2 files changed, 36 insertions(+), 12 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 7a0cbfe..f835153 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -1,26 +1,47 @@
import * as vscode from 'vscode';
import * as path from 'path';
import { SmellsViewProvider } from '../providers/SmellsViewProvider';
+import { refactorSmell as backendRefactorSmell } from '../api/backend'; // Import the backend function
-// 📌 Refactor Code Smells for a File
-export async function refactorSmellsByType(
+/**
+ * Handles the refactoring of a specific smell in a file.
+ *
+ * @param treeDataProvider - The tree data provider for updating the UI.
+ * @param filePath - The path of the file to refactor.
+ * @param smell - The smell to refactor.
+ */
+export async function refactorSmell(
treeDataProvider: SmellsViewProvider,
- fileUri: vscode.Uri | string,
+ filePath: string,
+ smell: Smell,
) {
- if (!fileUri) {
- vscode.window.showErrorMessage('Error: No file selected for refactoring.');
+ if (!filePath || !smell) {
+ vscode.window.showErrorMessage('Error: Invalid file path or smell.');
return;
}
- const filePath = typeof fileUri === 'string' ? fileUri : fileUri.fsPath;
vscode.window.showInformationMessage(
`Refactoring code smells in: ${path.basename(filePath)}`,
);
- // Simulate backend request
- setTimeout(() => {
+ try {
+ // Call the backend to refactor the smell
+ const refactoredData = await backendRefactorSmell(filePath, smell);
+
+ // Log the response from the backend
+ console.log('Refactoring response:', refactoredData);
+
+ // Notify the user
vscode.window.showInformationMessage(
- `Code smells refactored for: ${path.basename(filePath)}`,
+ `Refactoring successful! Energy saved: ${refactoredData.energySaved ?? 'N/A'} kg CO2`,
);
- }, 3000);
+
+ // Optionally, open the refactored file
+ const refactoredFilePath = refactoredData.targetFile.refactored;
+ const document = await vscode.workspace.openTextDocument(refactoredFilePath);
+ await vscode.window.showTextDocument(document);
+ } catch (error: any) {
+ console.error('Refactoring failed:', error.message);
+ vscode.window.showErrorMessage(`Refactoring failed: ${error.message}`);
+ }
}
diff --git a/src/extension.ts b/src/extension.ts
index 653a125..a73a3dc 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -15,6 +15,7 @@ import { checkServerStatus } from './api/backend';
import { FilterViewProvider } from './providers/FilterViewProvider';
import { SmellsCacheManager } from './context/SmellsCacheManager';
import { registerFileSaveListener } from './listeners/fileSaveListener';
+import { refactorSmell } from './commands/refactorSmell';
/**
* Activates the Eco-Optimizer extension and registers all necessary commands, providers, and listeners.
@@ -105,7 +106,8 @@ export function activate(context: vscode.ExtensionContext): void {
}
// Extract the smell ID from the fileUri string (e.g., "(aa7) R0913: Line 15")
- const smellIdMatch = fileUri.match(/\(([^)]+)\)/);
+ console.log('File URi:', fileUri);
+ const smellIdMatch = fileUri.match(/\(ID:\s*([^)]+)\)/);
const smellId = smellIdMatch ? smellIdMatch[1] : null;
if (!smellId) {
@@ -127,7 +129,8 @@ export function activate(context: vscode.ExtensionContext): void {
console.log('File Path:', filePath);
console.log('Smell Object:', smell);
- // Add additional logic here to handle refactoring
+ // Call the refactorSmell function
+ refactorSmell(smellsViewProvider, filePath, smell);
}),
);
From 40d2e8cc3420e688b5f5744debccbf642fc48310 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 00:08:01 -0400
Subject: [PATCH 099/215] added refactoring view
---
package.json | 5 ++
src/commands/refactorSmell.ts | 11 ++++
src/extension.ts | 22 +++++++-
.../RefactoringDetailsViewProvider.ts | 53 +++++++++++++++++++
4 files changed, 89 insertions(+), 2 deletions(-)
create mode 100644 src/providers/RefactoringDetailsViewProvider.ts
diff --git a/package.json b/package.json
index cdbd86d..0fa5dd9 100644
--- a/package.json
+++ b/package.json
@@ -137,6 +137,11 @@
"id": "ecooptimizer.filterView",
"name": "Filter Smells",
"icon": "assets/eco-icon.png"
+ },
+ {
+ "id": "ecooptimizer.refactoringDetails",
+ "name": "Refactoring Details",
+ "icon": "assets/eco-icon.png"
}
]
},
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index f835153..bc30576 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -1,17 +1,20 @@
import * as vscode from 'vscode';
import * as path from 'path';
import { SmellsViewProvider } from '../providers/SmellsViewProvider';
+import { RefactoringDetailsViewProvider } from '../providers/RefactoringDetailsViewProvider';
import { refactorSmell as backendRefactorSmell } from '../api/backend'; // Import the backend function
/**
* Handles the refactoring of a specific smell in a file.
*
* @param treeDataProvider - The tree data provider for updating the UI.
+ * @param refactoringDetailsViewProvider - The refactoring details view provider.
* @param filePath - The path of the file to refactor.
* @param smell - The smell to refactor.
*/
export async function refactorSmell(
treeDataProvider: SmellsViewProvider,
+ refactoringDetailsViewProvider: RefactoringDetailsViewProvider,
filePath: string,
smell: Smell,
) {
@@ -31,6 +34,11 @@ export async function refactorSmell(
// Log the response from the backend
console.log('Refactoring response:', refactoredData);
+ // Update the refactoring details view with the refactored file name
+ refactoringDetailsViewProvider.updateRefactoringDetails(
+ refactoredData.targetFile.refactored,
+ );
+
// Notify the user
vscode.window.showInformationMessage(
`Refactoring successful! Energy saved: ${refactoredData.energySaved ?? 'N/A'} kg CO2`,
@@ -43,5 +51,8 @@ export async function refactorSmell(
} catch (error: any) {
console.error('Refactoring failed:', error.message);
vscode.window.showErrorMessage(`Refactoring failed: ${error.message}`);
+
+ // Reset the refactoring details view on failure
+ refactoringDetailsViewProvider.resetRefactoringDetails();
}
}
diff --git a/src/extension.ts b/src/extension.ts
index a73a3dc..9cd5f49 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -16,6 +16,7 @@ import { FilterViewProvider } from './providers/FilterViewProvider';
import { SmellsCacheManager } from './context/SmellsCacheManager';
import { registerFileSaveListener } from './listeners/fileSaveListener';
import { refactorSmell } from './commands/refactorSmell';
+import { RefactoringDetailsViewProvider } from './providers/RefactoringDetailsViewProvider';
/**
* Activates the Eco-Optimizer extension and registers all necessary commands, providers, and listeners.
@@ -97,6 +98,19 @@ export function activate(context: vscode.ExtensionContext): void {
),
);
+ // Initialize the RefactoringDetailsViewProvider
+ const refactoringDetailsViewProvider = new RefactoringDetailsViewProvider();
+ const refactoringDetailsView = vscode.window.createTreeView(
+ 'ecooptimizer.refactoringDetails',
+ {
+ treeDataProvider: refactoringDetailsViewProvider,
+ },
+ );
+
+ // Reset the refactoring details view initially
+ refactoringDetailsViewProvider.resetRefactoringDetails();
+
+ // Register the refactorSmell command
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.refactorSmell', (fileUri) => {
// Ensure the fileUri is valid
@@ -106,7 +120,6 @@ export function activate(context: vscode.ExtensionContext): void {
}
// Extract the smell ID from the fileUri string (e.g., "(aa7) R0913: Line 15")
- console.log('File URi:', fileUri);
const smellIdMatch = fileUri.match(/\(ID:\s*([^)]+)\)/);
const smellId = smellIdMatch ? smellIdMatch[1] : null;
@@ -130,7 +143,12 @@ export function activate(context: vscode.ExtensionContext): void {
console.log('Smell Object:', smell);
// Call the refactorSmell function
- refactorSmell(smellsViewProvider, filePath, smell);
+ refactorSmell(
+ smellsViewProvider,
+ refactoringDetailsViewProvider,
+ filePath,
+ smell,
+ );
}),
);
diff --git a/src/providers/RefactoringDetailsViewProvider.ts b/src/providers/RefactoringDetailsViewProvider.ts
new file mode 100644
index 0000000..88800f1
--- /dev/null
+++ b/src/providers/RefactoringDetailsViewProvider.ts
@@ -0,0 +1,53 @@
+import * as vscode from 'vscode';
+
+export class RefactoringDetailsViewProvider
+ implements vscode.TreeDataProvider
+{
+ private _onDidChangeTreeData = new vscode.EventEmitter<
+ RefactoringDetailItem | undefined
+ >();
+ readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ private refactoringDetails: RefactoringDetailItem[] = [];
+
+ constructor() {}
+
+ /**
+ * Updates the refactoring details with the given file name.
+ * @param fileName - The name of the refactored file.
+ */
+ updateRefactoringDetails(fileName: string): void {
+ this.refactoringDetails = [
+ new RefactoringDetailItem('Refactored File', fileName),
+ ];
+ this._onDidChangeTreeData.fire(undefined); // Refresh the view
+ }
+
+ /**
+ * Resets the refactoring details to indicate no refactoring is in progress.
+ */
+ resetRefactoringDetails(): void {
+ this.refactoringDetails = [
+ new RefactoringDetailItem('Status', 'Refactoring not in progress'),
+ ];
+ this._onDidChangeTreeData.fire(undefined); // Refresh the view
+ }
+
+ getTreeItem(element: RefactoringDetailItem): vscode.TreeItem {
+ return element;
+ }
+
+ getChildren(element?: RefactoringDetailItem): RefactoringDetailItem[] {
+ if (element) {
+ return []; // No nested items
+ }
+ return this.refactoringDetails;
+ }
+}
+
+class RefactoringDetailItem extends vscode.TreeItem {
+ constructor(label: string, description: string) {
+ super(label, vscode.TreeItemCollapsibleState.None);
+ this.description = description;
+ }
+}
From 269ff46b5e71e3e069759eed511674c3f52a0883 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 00:16:15 -0400
Subject: [PATCH 100/215] added diff editor + accept and reject (not working)
---
package.json | 20 ++++++
src/commands/refactorSmell.ts | 16 +++--
src/extension.ts | 14 +++++
.../RefactoringDetailsViewProvider.ts | 62 +++++++++++++++++--
4 files changed, 102 insertions(+), 10 deletions(-)
diff --git a/package.json b/package.json
index 0fa5dd9..44c78df 100644
--- a/package.json
+++ b/package.json
@@ -213,6 +213,16 @@
"icon": "$(tools)",
"category": "Eco Optimizer"
},
+ {
+ "command": "ecooptimizer.acceptRefactoring",
+ "title": "Accept Refactoring",
+ "category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.rejectRefactoring",
+ "title": "Reject Refactoring",
+ "category": "Eco Optimizer"
+ },
{
"command": "ecooptimizer.jumpToSmell",
"title": "Jump to Smell in File",
@@ -245,6 +255,16 @@
"command": "ecooptimizer.deselectAllFilterSmells",
"when": "view == ecooptimizer.filterView",
"group": "resource"
+ },
+ {
+ "command": "ecooptimizer.acceptRefactoring",
+ "when": "view == ecooptimizer.refactoringDetails",
+ "group": "navigation"
+ },
+ {
+ "command": "ecooptimizer.rejectRefactoring",
+ "when": "view == ecooptimizer.refactoringDetails",
+ "group": "navigation"
}
],
"view/item/context": [
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index bc30576..ccfd673 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -37,17 +37,23 @@ export async function refactorSmell(
// Update the refactoring details view with the refactored file name
refactoringDetailsViewProvider.updateRefactoringDetails(
refactoredData.targetFile.refactored,
+ refactoredData.targetFile.original,
+ );
+
+ // Show a diff view between the original and refactored files
+ const originalUri = vscode.Uri.file(refactoredData.targetFile.original);
+ const refactoredUri = vscode.Uri.file(refactoredData.targetFile.refactored);
+ await vscode.commands.executeCommand(
+ 'vscode.diff',
+ originalUri,
+ refactoredUri,
+ 'Original ↔ Refactored',
);
// Notify the user
vscode.window.showInformationMessage(
`Refactoring successful! Energy saved: ${refactoredData.energySaved ?? 'N/A'} kg CO2`,
);
-
- // Optionally, open the refactored file
- const refactoredFilePath = refactoredData.targetFile.refactored;
- const document = await vscode.workspace.openTextDocument(refactoredFilePath);
- await vscode.window.showTextDocument(document);
} catch (error: any) {
console.error('Refactoring failed:', error.message);
vscode.window.showErrorMessage(`Refactoring failed: ${error.message}`);
diff --git a/src/extension.ts b/src/extension.ts
index 9cd5f49..cea6503 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -152,6 +152,20 @@ export function activate(context: vscode.ExtensionContext): void {
}),
);
+ // Register the acceptRefactoring command
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', () => {
+ refactoringDetailsViewProvider.acceptRefactoring();
+ }),
+ );
+
+ // Register the rejectRefactoring command
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.rejectRefactoring', () => {
+ refactoringDetailsViewProvider.rejectRefactoring();
+ }),
+ );
+
// Register the "Jump to Smell" command.
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.jumpToSmell', jumpToSmell),
diff --git a/src/providers/RefactoringDetailsViewProvider.ts b/src/providers/RefactoringDetailsViewProvider.ts
index 88800f1..18156a1 100644
--- a/src/providers/RefactoringDetailsViewProvider.ts
+++ b/src/providers/RefactoringDetailsViewProvider.ts
@@ -1,4 +1,5 @@
import * as vscode from 'vscode';
+import * as fs from 'fs';
export class RefactoringDetailsViewProvider
implements vscode.TreeDataProvider
@@ -9,16 +10,26 @@ export class RefactoringDetailsViewProvider
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
private refactoringDetails: RefactoringDetailItem[] = [];
+ private originalFilePath: string | undefined;
+ private refactoredFilePath: string | undefined;
constructor() {}
/**
- * Updates the refactoring details with the given file name.
- * @param fileName - The name of the refactored file.
+ * Updates the refactoring details with the given file names.
+ * @param refactoredFilePath - The path of the refactored file.
+ * @param originalFilePath - The path of the original file.
*/
- updateRefactoringDetails(fileName: string): void {
+ updateRefactoringDetails(
+ refactoredFilePath: string,
+ originalFilePath: string,
+ ): void {
+ this.refactoredFilePath = refactoredFilePath;
+ this.originalFilePath = originalFilePath;
+
this.refactoringDetails = [
- new RefactoringDetailItem('Refactored File', fileName),
+ new RefactoringDetailItem('Refactored File', refactoredFilePath, 'accept'),
+ new RefactoringDetailItem('Original File', originalFilePath, 'reject'),
];
this._onDidChangeTreeData.fire(undefined); // Refresh the view
}
@@ -27,6 +38,9 @@ export class RefactoringDetailsViewProvider
* Resets the refactoring details to indicate no refactoring is in progress.
*/
resetRefactoringDetails(): void {
+ this.refactoredFilePath = undefined;
+ this.originalFilePath = undefined;
+
this.refactoringDetails = [
new RefactoringDetailItem('Status', 'Refactoring not in progress'),
];
@@ -43,11 +57,49 @@ export class RefactoringDetailsViewProvider
}
return this.refactoringDetails;
}
+
+ /**
+ * Handles the accept action.
+ */
+ acceptRefactoring(): void {
+ if (this.refactoredFilePath && this.originalFilePath) {
+ // Replace the original file with the refactored file
+ fs.copyFileSync(this.refactoredFilePath, this.originalFilePath);
+ vscode.window.showInformationMessage('Refactoring accepted! Changes applied.');
+ } else {
+ vscode.window.showErrorMessage('No refactoring data available.');
+ }
+ }
+
+ /**
+ * Handles the reject action.
+ */
+ rejectRefactoring(): void {
+ vscode.window.showInformationMessage('Refactoring rejected! Changes discarded.');
+ }
}
class RefactoringDetailItem extends vscode.TreeItem {
- constructor(label: string, description: string) {
+ constructor(
+ label: string,
+ description: string,
+ public readonly action?: 'accept' | 'reject',
+ ) {
super(label, vscode.TreeItemCollapsibleState.None);
this.description = description;
+
+ if (action === 'accept') {
+ this.iconPath = new vscode.ThemeIcon('check');
+ this.command = {
+ command: 'ecooptimizer.acceptRefactoring',
+ title: 'Accept Refactoring',
+ };
+ } else if (action === 'reject') {
+ this.iconPath = new vscode.ThemeIcon('close');
+ this.command = {
+ command: 'ecooptimizer.rejectRefactoring',
+ title: 'Reject Refactoring',
+ };
+ }
}
}
From e6c4d0200bccff33cd1c9473e5a2b2cb12177247 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 01:21:52 -0400
Subject: [PATCH 101/215] refactoring: removed file path param
---
src/api/backend.ts | 23 +++++-----
src/commands/refactorSmell.ts | 17 +++----
src/extension.ts | 45 ++++++++++---------
.../RefactoringDetailsViewProvider.ts | 25 +----------
4 files changed, 46 insertions(+), 64 deletions(-)
diff --git a/src/api/backend.ts b/src/api/backend.ts
index 64d8ee5..a8b533d 100644
--- a/src/api/backend.ts
+++ b/src/api/backend.ts
@@ -30,7 +30,7 @@ export async function checkServerStatus(): Promise {
*/
export async function fetchSmells(
filePath: string,
- enabledSmells: Record>
+ enabledSmells: Record>,
): Promise<{ smells: Smell[]; status: number }> {
const url = `${BASE_URL}/smells`;
@@ -48,7 +48,7 @@ export async function fetchSmells(
if (!response.ok) {
throw new Error(
- `Backend request failed with status ${response.status}: ${response.statusText}`
+ `Backend request failed with status ${response.status}: ${response.statusText}`,
);
}
@@ -61,7 +61,7 @@ export async function fetchSmells(
return { smells: smellsList, status: response.status };
} catch (error: any) {
throw new Error(
- `Failed to connect to the backend: ${error.message}. Please check your network and try again.`
+ `Failed to connect to the backend: ${error.message}. Please check your network and try again.`,
);
}
}
@@ -69,33 +69,36 @@ export async function fetchSmells(
/**
* Sends a request to the backend to refactor a specific smell.
*
- * @param filePath - The absolute path to the file containing the smell.
* @param smell - The smell to refactor.
* @returns A promise resolving to the refactored data or throwing an error if unsuccessful.
*/
-export async function refactorSmell(
- filePath: string,
- smell: Smell
+export async function backendRefactorSmell(
+ smell: Smell,
): Promise {
const url = `${BASE_URL}/refactor`;
+ // Extract the file path from the smell object
+ const filePath = smell.path;
+
+ // Find the workspace folder containing the file
const workspaceFolder = vscode.workspace.workspaceFolders?.find((folder) =>
- filePath.includes(folder.uri.fsPath)
+ filePath.includes(folder.uri.fsPath),
);
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}`
+ `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 "${workspaceFolderPath}"`
+ `Eco: Initiating refactoring for smell "${smell.symbol}" in "${workspaceFolderPath}"`,
);
+ // Prepare the payload for the backend
const payload = {
source_dir: workspaceFolderPath,
smell,
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index ccfd673..6f40475 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -1,35 +1,30 @@
import * as vscode from 'vscode';
-import * as path from 'path';
+import { backendRefactorSmell } from '../api/backend';
import { SmellsViewProvider } from '../providers/SmellsViewProvider';
import { RefactoringDetailsViewProvider } from '../providers/RefactoringDetailsViewProvider';
-import { refactorSmell as backendRefactorSmell } from '../api/backend'; // Import the backend function
/**
- * Handles the refactoring of a specific smell in a file.
+ * Handles the refactoring of a specific smell.
*
* @param treeDataProvider - The tree data provider for updating the UI.
* @param refactoringDetailsViewProvider - The refactoring details view provider.
- * @param filePath - The path of the file to refactor.
* @param smell - The smell to refactor.
*/
export async function refactorSmell(
treeDataProvider: SmellsViewProvider,
refactoringDetailsViewProvider: RefactoringDetailsViewProvider,
- filePath: string,
smell: Smell,
) {
- if (!filePath || !smell) {
- vscode.window.showErrorMessage('Error: Invalid file path or smell.');
+ if (!smell) {
+ vscode.window.showErrorMessage('Error: Invalid smell.');
return;
}
- vscode.window.showInformationMessage(
- `Refactoring code smells in: ${path.basename(filePath)}`,
- );
+ vscode.window.showInformationMessage(`Refactoring code smell: ${smell.symbol}`);
try {
// Call the backend to refactor the smell
- const refactoredData = await backendRefactorSmell(filePath, smell);
+ const refactoredData = await backendRefactorSmell(smell);
// Log the response from the backend
console.log('Refactoring response:', refactoredData);
diff --git a/src/extension.ts b/src/extension.ts
index cea6503..e1e7777 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -4,6 +4,7 @@ import { envConfig } from './utils/envConfig';
import * as vscode from 'vscode';
import { configureWorkspace } from './commands/configureWorkspace';
+import * as fs from 'fs';
import { resetConfiguration } from './commands/resetConfiguration';
import { detectSmellsFile, detectSmellsFolder } from './commands/detectSmells';
import { openFile } from './commands/openFile';
@@ -113,12 +114,6 @@ export function activate(context: vscode.ExtensionContext): void {
// Register the refactorSmell command
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.refactorSmell', (fileUri) => {
- // Ensure the fileUri is valid
- if (!fileUri) {
- console.error('No file URI provided.');
- return;
- }
-
// Extract the smell ID from the fileUri string (e.g., "(aa7) R0913: Line 15")
const smellIdMatch = fileUri.match(/\(ID:\s*([^)]+)\)/);
const smellId = smellIdMatch ? smellIdMatch[1] : null;
@@ -135,34 +130,44 @@ export function activate(context: vscode.ExtensionContext): void {
return;
}
- // Get the file path from the smell object
- const filePath = smell.path;
-
- // Print the file path and smell object to the console
- console.log('File Path:', filePath);
+ // Print the smell object to the console
console.log('Smell Object:', smell);
- // Call the refactorSmell function
- refactorSmell(
- smellsViewProvider,
- refactoringDetailsViewProvider,
- filePath,
- smell,
- );
+ // Call the refactorSmell function with only the smell object
+ refactorSmell(smellsViewProvider, refactoringDetailsViewProvider, smell);
}),
);
// Register the acceptRefactoring command
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', () => {
- refactoringDetailsViewProvider.acceptRefactoring();
+ const refactoredFilePath = refactoringDetailsViewProvider.refactoredFilePath;
+ const originalFilePath = refactoringDetailsViewProvider.originalFilePath;
+
+ if (refactoredFilePath && originalFilePath) {
+ // Replace the original file with the refactored file
+ fs.copyFileSync(refactoredFilePath, originalFilePath);
+ vscode.window.showInformationMessage(
+ 'Refactoring accepted! Changes applied.',
+ );
+
+ // Reset the refactoring details view
+ refactoringDetailsViewProvider.resetRefactoringDetails();
+ } else {
+ vscode.window.showErrorMessage('No refactoring data available.');
+ }
}),
);
// Register the rejectRefactoring command
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.rejectRefactoring', () => {
- refactoringDetailsViewProvider.rejectRefactoring();
+ vscode.window.showInformationMessage(
+ 'Refactoring rejected! Changes discarded.',
+ );
+
+ // Reset the refactoring details view
+ refactoringDetailsViewProvider.resetRefactoringDetails();
}),
);
diff --git a/src/providers/RefactoringDetailsViewProvider.ts b/src/providers/RefactoringDetailsViewProvider.ts
index 18156a1..2b774ca 100644
--- a/src/providers/RefactoringDetailsViewProvider.ts
+++ b/src/providers/RefactoringDetailsViewProvider.ts
@@ -1,5 +1,4 @@
import * as vscode from 'vscode';
-import * as fs from 'fs';
export class RefactoringDetailsViewProvider
implements vscode.TreeDataProvider
@@ -10,8 +9,8 @@ export class RefactoringDetailsViewProvider
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
private refactoringDetails: RefactoringDetailItem[] = [];
- private originalFilePath: string | undefined;
- private refactoredFilePath: string | undefined;
+ public originalFilePath: string | undefined;
+ public refactoredFilePath: string | undefined;
constructor() {}
@@ -57,26 +56,6 @@ export class RefactoringDetailsViewProvider
}
return this.refactoringDetails;
}
-
- /**
- * Handles the accept action.
- */
- acceptRefactoring(): void {
- if (this.refactoredFilePath && this.originalFilePath) {
- // Replace the original file with the refactored file
- fs.copyFileSync(this.refactoredFilePath, this.originalFilePath);
- vscode.window.showInformationMessage('Refactoring accepted! Changes applied.');
- } else {
- vscode.window.showErrorMessage('No refactoring data available.');
- }
- }
-
- /**
- * Handles the reject action.
- */
- rejectRefactoring(): void {
- vscode.window.showInformationMessage('Refactoring rejected! Changes discarded.');
- }
}
class RefactoringDetailItem extends vscode.TreeItem {
From 1927f8a516f3b53d779908482dd8171925414132 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 01:47:50 -0400
Subject: [PATCH 102/215] added accept reject icons
---
package.json | 16 ++++---
src/commands/refactorSmell.ts | 45 +++++++++++++++++++
src/extension.ts | 9 +++-
.../RefactoringDetailsViewProvider.ts | 41 ++++++++---------
4 files changed, 80 insertions(+), 31 deletions(-)
diff --git a/package.json b/package.json
index 44c78df..d293dcf 100644
--- a/package.json
+++ b/package.json
@@ -147,9 +147,9 @@
},
"viewsWelcome": [
{
- "view": "ecooptimizer.view",
- "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.refactoringDetails",
+ "contents": "Refactoring is currently not in progress.",
+ "when": "!refactoringInProgress"
}
],
"commands": [
@@ -216,12 +216,14 @@
{
"command": "ecooptimizer.acceptRefactoring",
"title": "Accept Refactoring",
- "category": "Eco Optimizer"
+ "category": "Eco Optimizer",
+ "icon": "$(check)"
},
{
"command": "ecooptimizer.rejectRefactoring",
"title": "Reject Refactoring",
- "category": "Eco Optimizer"
+ "category": "Eco Optimizer",
+ "icon": "$(close)"
},
{
"command": "ecooptimizer.jumpToSmell",
@@ -258,12 +260,12 @@
},
{
"command": "ecooptimizer.acceptRefactoring",
- "when": "view == ecooptimizer.refactoringDetails",
+ "when": "view == ecooptimizer.refactoringDetails && refactoringInProgress",
"group": "navigation"
},
{
"command": "ecooptimizer.rejectRefactoring",
- "when": "view == ecooptimizer.refactoringDetails",
+ "when": "view == ecooptimizer.refactoringDetails && refactoringInProgress",
"group": "navigation"
}
],
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 6f40475..f4905a3 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -45,15 +45,60 @@ export async function refactorSmell(
'Original ↔ Refactored',
);
+ // Set a context key to track that refactoring is in progress
+ vscode.commands.executeCommand('setContext', 'refactoringInProgress', true);
+
+ // Listen for the diff editor being closed manually
+ const closeListener = vscode.window.onDidChangeVisibleTextEditors((editors) => {
+ const diffEditorStillOpen = editors.some(
+ (editor) =>
+ editor.document.uri.toString() === originalUri.toString() ||
+ editor.document.uri.toString() === refactoredUri.toString(),
+ );
+
+ if (!diffEditorStillOpen) {
+ // Show a confirmation popup if the diff editor is closed manually
+ vscode.window
+ .showWarningMessage(
+ 'You need to accept or reject the refactoring. Do you want to stop refactoring?',
+ { modal: true },
+ 'Stop Refactoring',
+ )
+ .then((choice) => {
+ if (choice === 'Stop Refactoring') {
+ // Reset the refactoring state
+ refactoringDetailsViewProvider.resetRefactoringDetails();
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'refactoringInProgress',
+ false,
+ );
+ } else {
+ // Reopen the diff editor
+ vscode.commands.executeCommand(
+ 'vscode.diff',
+ originalUri,
+ refactoredUri,
+ 'Original ↔ Refactored',
+ );
+ }
+ });
+ }
+ });
+
// Notify the user
vscode.window.showInformationMessage(
`Refactoring successful! Energy saved: ${refactoredData.energySaved ?? 'N/A'} kg CO2`,
);
+
+ // Return the close listener so it can be disposed later
+ return closeListener;
} catch (error: any) {
console.error('Refactoring failed:', error.message);
vscode.window.showErrorMessage(`Refactoring failed: ${error.message}`);
// Reset the refactoring details view on failure
refactoringDetailsViewProvider.resetRefactoringDetails();
+ vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
}
}
diff --git a/src/extension.ts b/src/extension.ts
index e1e7777..14aa93e 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -137,7 +137,6 @@ export function activate(context: vscode.ExtensionContext): void {
refactorSmell(smellsViewProvider, refactoringDetailsViewProvider, smell);
}),
);
-
// Register the acceptRefactoring command
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', () => {
@@ -151,8 +150,12 @@ export function activate(context: vscode.ExtensionContext): void {
'Refactoring accepted! Changes applied.',
);
+ // Close the diff editor
+ vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+
// Reset the refactoring details view
refactoringDetailsViewProvider.resetRefactoringDetails();
+ vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
} else {
vscode.window.showErrorMessage('No refactoring data available.');
}
@@ -166,8 +169,12 @@ export function activate(context: vscode.ExtensionContext): void {
'Refactoring rejected! Changes discarded.',
);
+ // Close the diff editor
+ vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+
// Reset the refactoring details view
refactoringDetailsViewProvider.resetRefactoringDetails();
+ vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
}),
);
diff --git a/src/providers/RefactoringDetailsViewProvider.ts b/src/providers/RefactoringDetailsViewProvider.ts
index 2b774ca..1f39fff 100644
--- a/src/providers/RefactoringDetailsViewProvider.ts
+++ b/src/providers/RefactoringDetailsViewProvider.ts
@@ -12,7 +12,10 @@ export class RefactoringDetailsViewProvider
public originalFilePath: string | undefined;
public refactoredFilePath: string | undefined;
- constructor() {}
+ constructor() {
+ // Initialize with the welcome view
+ this.resetRefactoringDetails();
+ }
/**
* Updates the refactoring details with the given file names.
@@ -26,9 +29,15 @@ export class RefactoringDetailsViewProvider
this.refactoredFilePath = refactoredFilePath;
this.originalFilePath = originalFilePath;
+ // Convert the absolute path of the original file to a relative path for display
+ const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
+ const relativeOriginalPath = workspaceFolder
+ ? vscode.workspace.asRelativePath(originalFilePath)
+ : originalFilePath;
+
+ // Update the tree view with only the original file's relative path
this.refactoringDetails = [
- new RefactoringDetailItem('Refactored File', refactoredFilePath, 'accept'),
- new RefactoringDetailItem('Original File', originalFilePath, 'reject'),
+ new RefactoringDetailItem('Original File', relativeOriginalPath),
];
this._onDidChangeTreeData.fire(undefined); // Refresh the view
}
@@ -40,8 +49,12 @@ export class RefactoringDetailsViewProvider
this.refactoredFilePath = undefined;
this.originalFilePath = undefined;
+ // Set the welcome view
this.refactoringDetails = [
- new RefactoringDetailItem('Status', 'Refactoring not in progress'),
+ new RefactoringDetailItem(
+ 'Status',
+ 'Refactoring is currently not in progress.',
+ ),
];
this._onDidChangeTreeData.fire(undefined); // Refresh the view
}
@@ -59,26 +72,8 @@ export class RefactoringDetailsViewProvider
}
class RefactoringDetailItem extends vscode.TreeItem {
- constructor(
- label: string,
- description: string,
- public readonly action?: 'accept' | 'reject',
- ) {
+ constructor(label: string, description: string) {
super(label, vscode.TreeItemCollapsibleState.None);
this.description = description;
-
- if (action === 'accept') {
- this.iconPath = new vscode.ThemeIcon('check');
- this.command = {
- command: 'ecooptimizer.acceptRefactoring',
- title: 'Accept Refactoring',
- };
- } else if (action === 'reject') {
- this.iconPath = new vscode.ThemeIcon('close');
- this.command = {
- command: 'ecooptimizer.rejectRefactoring',
- title: 'Reject Refactoring',
- };
- }
}
}
From 727a2997f7e151faada1321f7c813691372e5bbe Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 02:19:31 -0400
Subject: [PATCH 103/215] more refactoring
---
package.json | 17 ++++---
src/extension.ts | 48 ++++++++++++++-----
.../RefactoringDetailsViewProvider.ts | 17 +++----
3 files changed, 53 insertions(+), 29 deletions(-)
diff --git a/package.json b/package.json
index d293dcf..bd4d82a 100644
--- a/package.json
+++ b/package.json
@@ -128,6 +128,11 @@
},
"views": {
"ecooptimizer": [
+ {
+ "id": "ecooptimizer.refactoringDetails",
+ "name": "Refactoring Details",
+ "icon": "assets/eco-icon.png"
+ },
{
"id": "ecooptimizer.view",
"name": "Code Smells",
@@ -137,19 +142,19 @@
"id": "ecooptimizer.filterView",
"name": "Filter Smells",
"icon": "assets/eco-icon.png"
- },
- {
- "id": "ecooptimizer.refactoringDetails",
- "name": "Refactoring Details",
- "icon": "assets/eco-icon.png"
}
]
},
"viewsWelcome": [
{
"view": "ecooptimizer.refactoringDetails",
- "contents": "Refactoring is currently not in progress.",
+ "contents": "Refactoring is currently not in progress. Try selecting a smell in the Code Smells view to start refactoring.",
"when": "!refactoringInProgress"
+ },
+ {
+ "view": "ecooptimizer.view",
+ "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"
}
],
"commands": [
diff --git a/src/extension.ts b/src/extension.ts
index 14aa93e..03e3fd0 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -137,6 +137,7 @@ export function activate(context: vscode.ExtensionContext): void {
refactorSmell(smellsViewProvider, refactoringDetailsViewProvider, smell);
}),
);
+
// Register the acceptRefactoring command
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', () => {
@@ -144,18 +145,35 @@ export function activate(context: vscode.ExtensionContext): void {
const originalFilePath = refactoringDetailsViewProvider.originalFilePath;
if (refactoredFilePath && originalFilePath) {
- // Replace the original file with the refactored file
- fs.copyFileSync(refactoredFilePath, originalFilePath);
- vscode.window.showInformationMessage(
- 'Refactoring accepted! Changes applied.',
- );
-
- // Close the diff editor
- vscode.commands.executeCommand('workbench.action.closeActiveEditor');
-
- // Reset the refactoring details view
- refactoringDetailsViewProvider.resetRefactoringDetails();
- vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
+ try {
+ // Replace the original file with the refactored file
+ fs.copyFileSync(refactoredFilePath, originalFilePath);
+ vscode.window.showInformationMessage(
+ 'Refactoring accepted! Changes applied.',
+ );
+
+ // Close the diff editor
+ vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+
+ // Reset the refactoring details view
+ refactoringDetailsViewProvider.resetRefactoringDetails();
+ vscode.commands.executeCommand(
+ 'setContext',
+ 'refactoringInProgress',
+ false,
+ );
+
+ // Close the refactoring details view
+ vscode.commands.executeCommand(
+ 'workbench.action.closeView',
+ 'ecooptimizer.refactoringDetails',
+ );
+ } catch (error) {
+ console.error('Failed to accept refactoring:', error);
+ vscode.window.showErrorMessage(
+ 'Failed to accept refactoring. Please try again.',
+ );
+ }
} else {
vscode.window.showErrorMessage('No refactoring data available.');
}
@@ -175,6 +193,12 @@ export function activate(context: vscode.ExtensionContext): void {
// Reset the refactoring details view
refactoringDetailsViewProvider.resetRefactoringDetails();
vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
+
+ // Close the refactoring details view
+ vscode.commands.executeCommand(
+ 'workbench.action.closeView',
+ 'ecooptimizer.refactoringDetails',
+ );
}),
);
diff --git a/src/providers/RefactoringDetailsViewProvider.ts b/src/providers/RefactoringDetailsViewProvider.ts
index 1f39fff..5d8c2e4 100644
--- a/src/providers/RefactoringDetailsViewProvider.ts
+++ b/src/providers/RefactoringDetailsViewProvider.ts
@@ -10,10 +10,10 @@ export class RefactoringDetailsViewProvider
private refactoringDetails: RefactoringDetailItem[] = [];
public originalFilePath: string | undefined;
- public refactoredFilePath: string | undefined;
+ public refactoredFilePath: string | undefined; // Add this property
constructor() {
- // Initialize with the welcome view
+ // Initialize with an empty state
this.resetRefactoringDetails();
}
@@ -26,7 +26,7 @@ export class RefactoringDetailsViewProvider
refactoredFilePath: string,
originalFilePath: string,
): void {
- this.refactoredFilePath = refactoredFilePath;
+ this.refactoredFilePath = refactoredFilePath; // Set the refactored file path
this.originalFilePath = originalFilePath;
// Convert the absolute path of the original file to a relative path for display
@@ -46,16 +46,11 @@ export class RefactoringDetailsViewProvider
* Resets the refactoring details to indicate no refactoring is in progress.
*/
resetRefactoringDetails(): void {
- this.refactoredFilePath = undefined;
+ this.refactoredFilePath = undefined; // Reset the refactored file path
this.originalFilePath = undefined;
- // Set the welcome view
- this.refactoringDetails = [
- new RefactoringDetailItem(
- 'Status',
- 'Refactoring is currently not in progress.',
- ),
- ];
+ // Clear the tree view
+ this.refactoringDetails = [];
this._onDidChangeTreeData.fire(undefined); // Refresh the view
}
From 3d9fd28075c5556a066052ec5ca4b90732edba75 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 02:56:19 -0400
Subject: [PATCH 104/215] more refactoring - open diff editor command
---
package.json | 11 +++++
src/commands/refactorSmell.ts | 43 +------------------
src/extension.ts | 35 ++++++++++-----
.../RefactoringDetailsViewProvider.ts | 7 +++
4 files changed, 43 insertions(+), 53 deletions(-)
diff --git a/package.json b/package.json
index bd4d82a..e935595 100644
--- a/package.json
+++ b/package.json
@@ -239,6 +239,11 @@
"command": "ecooptimizer.wipeWorkCache",
"title": "Clear Smells Cache",
"category": "Eco Optimizer"
+ },
+ {
+ "command": "ecooptimizer.openDiffEditor",
+ "title": "Open Diff Editor",
+ "category": "Eco Optimizer"
}
],
"menus": {
@@ -299,6 +304,12 @@
"command": "ecooptimizer.refactorSmell",
"when": "viewItem == ecoOptimizerSmell",
"group": "inline"
+ },
+ {
+ "command": "ecooptimizer.openDiffEditor",
+ "when": "viewItem == ecoOptimizerFile",
+ "group": "inline",
+ "icon": "$(diff)"
}
]
}
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index f4905a3..63f1f1f 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -42,57 +42,16 @@ export async function refactorSmell(
'vscode.diff',
originalUri,
refactoredUri,
- 'Original ↔ Refactored',
+ 'Refactoring Comparison',
);
// Set a context key to track that refactoring is in progress
vscode.commands.executeCommand('setContext', 'refactoringInProgress', true);
- // Listen for the diff editor being closed manually
- const closeListener = vscode.window.onDidChangeVisibleTextEditors((editors) => {
- const diffEditorStillOpen = editors.some(
- (editor) =>
- editor.document.uri.toString() === originalUri.toString() ||
- editor.document.uri.toString() === refactoredUri.toString(),
- );
-
- if (!diffEditorStillOpen) {
- // Show a confirmation popup if the diff editor is closed manually
- vscode.window
- .showWarningMessage(
- 'You need to accept or reject the refactoring. Do you want to stop refactoring?',
- { modal: true },
- 'Stop Refactoring',
- )
- .then((choice) => {
- if (choice === 'Stop Refactoring') {
- // Reset the refactoring state
- refactoringDetailsViewProvider.resetRefactoringDetails();
- vscode.commands.executeCommand(
- 'setContext',
- 'refactoringInProgress',
- false,
- );
- } else {
- // Reopen the diff editor
- vscode.commands.executeCommand(
- 'vscode.diff',
- originalUri,
- refactoredUri,
- 'Original ↔ Refactored',
- );
- }
- });
- }
- });
-
// Notify the user
vscode.window.showInformationMessage(
`Refactoring successful! Energy saved: ${refactoredData.energySaved ?? 'N/A'} kg CO2`,
);
-
- // Return the close listener so it can be disposed later
- return closeListener;
} catch (error: any) {
console.error('Refactoring failed:', error.message);
vscode.window.showErrorMessage(`Refactoring failed: ${error.message}`);
diff --git a/src/extension.ts b/src/extension.ts
index 03e3fd0..50e7aaf 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -162,12 +162,6 @@ export function activate(context: vscode.ExtensionContext): void {
'refactoringInProgress',
false,
);
-
- // Close the refactoring details view
- vscode.commands.executeCommand(
- 'workbench.action.closeView',
- 'ecooptimizer.refactoringDetails',
- );
} catch (error) {
console.error('Failed to accept refactoring:', error);
vscode.window.showErrorMessage(
@@ -193,12 +187,31 @@ export function activate(context: vscode.ExtensionContext): void {
// Reset the refactoring details view
refactoringDetailsViewProvider.resetRefactoringDetails();
vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
+ }),
+ );
- // Close the refactoring details view
- vscode.commands.executeCommand(
- 'workbench.action.closeView',
- 'ecooptimizer.refactoringDetails',
- );
+ // Register the command to open the diff editor
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.openDiffEditor', (fileUri) => {
+ const refactoredFilePath = refactoringDetailsViewProvider.refactoredFilePath;
+ const originalFilePath = refactoringDetailsViewProvider.originalFilePath;
+
+ if (refactoredFilePath && originalFilePath) {
+ // Get the file name from the original file path
+ const fileName = originalFilePath.split('/').pop() || 'file';
+
+ // Show the diff editor
+ const originalUri = vscode.Uri.file(originalFilePath);
+ const refactoredUri = vscode.Uri.file(refactoredFilePath);
+ vscode.commands.executeCommand(
+ 'vscode.diff',
+ originalUri,
+ refactoredUri,
+ `${fileName} (original) ↔ ${fileName} (refactored)`,
+ );
+ } else {
+ vscode.window.showErrorMessage('No refactoring data available.');
+ }
}),
);
diff --git a/src/providers/RefactoringDetailsViewProvider.ts b/src/providers/RefactoringDetailsViewProvider.ts
index 5d8c2e4..e7e39f3 100644
--- a/src/providers/RefactoringDetailsViewProvider.ts
+++ b/src/providers/RefactoringDetailsViewProvider.ts
@@ -70,5 +70,12 @@ class RefactoringDetailItem extends vscode.TreeItem {
constructor(label: string, description: string) {
super(label, vscode.TreeItemCollapsibleState.None);
this.description = description;
+
+ // Add a command to open the diff editor when the item is clicked
+ this.command = {
+ command: 'ecooptimizer.openDiffEditor',
+ title: 'Open Diff Editor',
+ arguments: [description], // Pass the file path as an argument
+ };
}
}
From c26df542e5bb824e25d0e793a33652214f6ca59e Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 03:22:57 -0400
Subject: [PATCH 105/215] refactor - fixed sidebar dropdown with affected files
---
src/commands/refactorSmell.ts | 16 ++-
src/extension.ts | 120 +++++++++---------
.../RefactoringDetailsViewProvider.ts | 103 +++++++++++----
3 files changed, 146 insertions(+), 93 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 63f1f1f..44d7b40 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -29,20 +29,22 @@ export async function refactorSmell(
// Log the response from the backend
console.log('Refactoring response:', refactoredData);
- // Update the refactoring details view with the refactored file name
+ // Update the refactoring details view with the target file and affected files
refactoringDetailsViewProvider.updateRefactoringDetails(
- refactoredData.targetFile.refactored,
- refactoredData.targetFile.original,
+ refactoredData.targetFile,
+ refactoredData.affectedFiles,
);
- // Show a diff view between the original and refactored files
- const originalUri = vscode.Uri.file(refactoredData.targetFile.original);
- const refactoredUri = vscode.Uri.file(refactoredData.targetFile.refactored);
+ // Show a diff view for the target file
+ const targetFile = refactoredData.targetFile;
+ const fileName = targetFile.original.split('/').pop() || 'file';
+ const originalUri = vscode.Uri.file(targetFile.original);
+ const refactoredUri = vscode.Uri.file(targetFile.refactored);
await vscode.commands.executeCommand(
'vscode.diff',
originalUri,
refactoredUri,
- 'Refactoring Comparison',
+ `${fileName} (original) ↔ ${fileName} (refactored)`,
);
// Set a context key to track that refactoring is in progress
diff --git a/src/extension.ts b/src/extension.ts
index 50e7aaf..f46780a 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -4,7 +4,6 @@ import { envConfig } from './utils/envConfig';
import * as vscode from 'vscode';
import { configureWorkspace } from './commands/configureWorkspace';
-import * as fs from 'fs';
import { resetConfiguration } from './commands/resetConfiguration';
import { detectSmellsFile, detectSmellsFolder } from './commands/detectSmells';
import { openFile } from './commands/openFile';
@@ -138,66 +137,65 @@ export function activate(context: vscode.ExtensionContext): void {
}),
);
- // Register the acceptRefactoring command
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', () => {
- const refactoredFilePath = refactoringDetailsViewProvider.refactoredFilePath;
- const originalFilePath = refactoringDetailsViewProvider.originalFilePath;
-
- if (refactoredFilePath && originalFilePath) {
- try {
- // Replace the original file with the refactored file
- fs.copyFileSync(refactoredFilePath, originalFilePath);
- vscode.window.showInformationMessage(
- 'Refactoring accepted! Changes applied.',
- );
-
- // Close the diff editor
- vscode.commands.executeCommand('workbench.action.closeActiveEditor');
-
- // Reset the refactoring details view
- refactoringDetailsViewProvider.resetRefactoringDetails();
- vscode.commands.executeCommand(
- 'setContext',
- 'refactoringInProgress',
- false,
- );
- } catch (error) {
- console.error('Failed to accept refactoring:', error);
- vscode.window.showErrorMessage(
- 'Failed to accept refactoring. Please try again.',
- );
- }
- } else {
- vscode.window.showErrorMessage('No refactoring data available.');
- }
- }),
- );
-
- // Register the rejectRefactoring command
- context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.rejectRefactoring', () => {
- vscode.window.showInformationMessage(
- 'Refactoring rejected! Changes discarded.',
- );
-
- // Close the diff editor
- vscode.commands.executeCommand('workbench.action.closeActiveEditor');
-
- // Reset the refactoring details view
- refactoringDetailsViewProvider.resetRefactoringDetails();
- vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
- }),
- );
+ // // Register the acceptRefactoring command
+ // context.subscriptions.push(
+ // vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', () => {
+ // const refactoredFilePath = refactoringDetailsViewProvider.refactoredFilePath;
+ // const originalFilePath = refactoringDetailsViewProvider.originalFilePath;
+
+ // if (refactoredFilePath && originalFilePath) {
+ // try {
+ // // Replace the original file with the refactored file
+ // fs.copyFileSync(refactoredFilePath, originalFilePath);
+ // vscode.window.showInformationMessage(
+ // 'Refactoring accepted! Changes applied.',
+ // );
+
+ // // Close the diff editor
+ // vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+
+ // // Reset the refactoring details view
+ // refactoringDetailsViewProvider.resetRefactoringDetails();
+ // vscode.commands.executeCommand(
+ // 'setContext',
+ // 'refactoringInProgress',
+ // false,
+ // );
+ // } catch (error) {
+ // console.error('Failed to accept refactoring:', error);
+ // vscode.window.showErrorMessage(
+ // 'Failed to accept refactoring. Please try again.',
+ // );
+ // }
+ // } else {
+ // vscode.window.showErrorMessage('No refactoring data available.');
+ // }
+ // }),
+ // );
+
+ // // Register the rejectRefactoring command
+ // context.subscriptions.push(
+ // vscode.commands.registerCommand('ecooptimizer.rejectRefactoring', () => {
+ // vscode.window.showInformationMessage(
+ // 'Refactoring rejected! Changes discarded.',
+ // );
+
+ // // Close the diff editor
+ // vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+
+ // // Reset the refactoring details view
+ // refactoringDetailsViewProvider.resetRefactoringDetails();
+ // vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
+ // }),
+ // );
+ // Register the command to open the diff editor
// Register the command to open the diff editor
context.subscriptions.push(
- vscode.commands.registerCommand('ecooptimizer.openDiffEditor', (fileUri) => {
- const refactoredFilePath = refactoringDetailsViewProvider.refactoredFilePath;
- const originalFilePath = refactoringDetailsViewProvider.originalFilePath;
-
- if (refactoredFilePath && originalFilePath) {
- // Get the file name from the original file path
+ vscode.commands.registerCommand(
+ 'ecooptimizer.openDiffEditor',
+ (originalFilePath: string, refactoredFilePath: string) => {
+ // Get the file name for the diff editor title
const fileName = originalFilePath.split('/').pop() || 'file';
// Show the diff editor
@@ -209,10 +207,8 @@ export function activate(context: vscode.ExtensionContext): void {
refactoredUri,
`${fileName} (original) ↔ ${fileName} (refactored)`,
);
- } else {
- vscode.window.showErrorMessage('No refactoring data available.');
- }
- }),
+ },
+ ),
);
// Register the "Jump to Smell" command.
diff --git a/src/providers/RefactoringDetailsViewProvider.ts b/src/providers/RefactoringDetailsViewProvider.ts
index e7e39f3..432af54 100644
--- a/src/providers/RefactoringDetailsViewProvider.ts
+++ b/src/providers/RefactoringDetailsViewProvider.ts
@@ -1,4 +1,5 @@
import * as vscode from 'vscode';
+import * as path from 'path';
export class RefactoringDetailsViewProvider
implements vscode.TreeDataProvider
@@ -9,8 +10,8 @@ export class RefactoringDetailsViewProvider
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
private refactoringDetails: RefactoringDetailItem[] = [];
- public originalFilePath: string | undefined;
- public refactoredFilePath: string | undefined; // Add this property
+ public targetFile: { original: string; refactored: string } | undefined;
+ public affectedFiles: { original: string; refactored: string }[] = [];
constructor() {
// Initialize with an empty state
@@ -18,27 +19,56 @@ export class RefactoringDetailsViewProvider
}
/**
- * Updates the refactoring details with the given file names.
- * @param refactoredFilePath - The path of the refactored file.
- * @param originalFilePath - The path of the original file.
+ * Updates the refactoring details with the given target file and affected files.
+ * @param targetFile - The target file (original and refactored paths).
+ * @param affectedFiles - The list of affected files (original and refactored paths).
*/
updateRefactoringDetails(
- refactoredFilePath: string,
- originalFilePath: string,
+ targetFile: { original: string; refactored: string },
+ affectedFiles: { original: string; refactored: string }[],
): void {
- this.refactoredFilePath = refactoredFilePath; // Set the refactored file path
- this.originalFilePath = originalFilePath;
+ this.targetFile = targetFile;
+ this.affectedFiles = affectedFiles;
- // Convert the absolute path of the original file to a relative path for display
+ // Convert the absolute paths to relative paths for display
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
- const relativeOriginalPath = workspaceFolder
- ? vscode.workspace.asRelativePath(originalFilePath)
- : originalFilePath;
+ const relativeTargetFile = workspaceFolder
+ ? vscode.workspace.asRelativePath(targetFile.original)
+ : targetFile.original;
- // Update the tree view with only the original file's relative path
+ const relativeAffectedFiles = affectedFiles.map((file) =>
+ workspaceFolder
+ ? vscode.workspace.asRelativePath(file.original)
+ : file.original,
+ );
+
+ // Create the tree view items
this.refactoringDetails = [
- new RefactoringDetailItem('Original File', relativeOriginalPath),
+ new RefactoringDetailItem(
+ path.basename(targetFile.original), // File name as label
+ 'Target File', // Description
+ targetFile.original,
+ targetFile.refactored,
+ true, // This is a parent item (collapsible)
+ ),
];
+
+ // Add affected files as child items
+ if (affectedFiles.length > 0) {
+ this.refactoringDetails.push(
+ ...affectedFiles.map(
+ (file) =>
+ new RefactoringDetailItem(
+ path.basename(file.original), // File name as label
+ 'Affected File', // Description
+ file.original,
+ file.refactored,
+ false, // This is a child item (not collapsible)
+ ),
+ ),
+ );
+ }
+
this._onDidChangeTreeData.fire(undefined); // Refresh the view
}
@@ -46,8 +76,8 @@ export class RefactoringDetailsViewProvider
* Resets the refactoring details to indicate no refactoring is in progress.
*/
resetRefactoringDetails(): void {
- this.refactoredFilePath = undefined; // Reset the refactored file path
- this.originalFilePath = undefined;
+ this.targetFile = undefined;
+ this.affectedFiles = [];
// Clear the tree view
this.refactoringDetails = [];
@@ -60,22 +90,47 @@ export class RefactoringDetailsViewProvider
getChildren(element?: RefactoringDetailItem): RefactoringDetailItem[] {
if (element) {
- return []; // No nested items
+ // If this is the parent item (Target File), return the affected files as children
+ if (element.isParent) {
+ return this.affectedFiles.map(
+ (file) =>
+ new RefactoringDetailItem(
+ path.basename(file.original), // File name as label
+ 'Affected File', // Description
+ file.original,
+ file.refactored,
+ false, // This is a child item (not collapsible)
+ ),
+ );
+ }
+ return []; // No nested items for child items
}
- return this.refactoringDetails;
+ // If no element is provided, return the parent item (Target File)
+ return this.refactoringDetails.filter((item) => item.isParent);
}
}
class RefactoringDetailItem extends vscode.TreeItem {
- constructor(label: string, description: string) {
- super(label, vscode.TreeItemCollapsibleState.None);
- this.description = description;
+ constructor(
+ label: string, // File name
+ description: string, // "Target File" or "Affected File"
+ public readonly originalFilePath: string,
+ public readonly refactoredFilePath: string,
+ public readonly isParent: boolean = false, // Whether this is a parent item
+ ) {
+ super(
+ label,
+ isParent
+ ? vscode.TreeItemCollapsibleState.Collapsed // Parent item is collapsible
+ : vscode.TreeItemCollapsibleState.None, // Child item is not collapsible
+ );
+ this.description = description; // Set the description
- // Add a command to open the diff editor when the item is clicked
+ // Add a command to open the diff editor for both parent and child items
this.command = {
command: 'ecooptimizer.openDiffEditor',
title: 'Open Diff Editor',
- arguments: [description], // Pass the file path as an argument
+ arguments: [originalFilePath, refactoredFilePath],
};
}
}
From 136b35cfacf61fe15c4ca1b8f36ae4166922f031 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 03:47:17 -0400
Subject: [PATCH 106/215] refactor - fixed diff editor to not open in preview
---
package.json | 6 ------
src/commands/refactorSmell.ts | 8 ++++++--
src/extension.ts | 11 +++++++----
3 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/package.json b/package.json
index e935595..58a4124 100644
--- a/package.json
+++ b/package.json
@@ -304,12 +304,6 @@
"command": "ecooptimizer.refactorSmell",
"when": "viewItem == ecoOptimizerSmell",
"group": "inline"
- },
- {
- "command": "ecooptimizer.openDiffEditor",
- "when": "viewItem == ecoOptimizerFile",
- "group": "inline",
- "icon": "$(diff)"
}
]
}
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 44d7b40..1947227 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -2,6 +2,7 @@ import * as vscode from 'vscode';
import { backendRefactorSmell } from '../api/backend';
import { SmellsViewProvider } from '../providers/SmellsViewProvider';
import { RefactoringDetailsViewProvider } from '../providers/RefactoringDetailsViewProvider';
+import path from 'path';
/**
* Handles the refactoring of a specific smell.
@@ -37,14 +38,17 @@ export async function refactorSmell(
// Show a diff view for the target file
const targetFile = refactoredData.targetFile;
- const fileName = targetFile.original.split('/').pop() || 'file';
+ const fileName = path.basename(targetFile.original);
const originalUri = vscode.Uri.file(targetFile.original);
const refactoredUri = vscode.Uri.file(targetFile.refactored);
await vscode.commands.executeCommand(
'vscode.diff',
originalUri,
refactoredUri,
- `${fileName} (original) ↔ ${fileName} (refactored)`,
+ `Refactoring Comparison (${fileName})`,
+ {
+ preview: false,
+ },
);
// Set a context key to track that refactoring is in progress
diff --git a/src/extension.ts b/src/extension.ts
index f46780a..86fbcc9 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -17,6 +17,7 @@ import { SmellsCacheManager } from './context/SmellsCacheManager';
import { registerFileSaveListener } from './listeners/fileSaveListener';
import { refactorSmell } from './commands/refactorSmell';
import { RefactoringDetailsViewProvider } from './providers/RefactoringDetailsViewProvider';
+import path from 'path';
/**
* Activates the Eco-Optimizer extension and registers all necessary commands, providers, and listeners.
@@ -189,23 +190,25 @@ export function activate(context: vscode.ExtensionContext): void {
// }),
// );
- // Register the command to open the diff editor
// Register the command to open the diff editor
context.subscriptions.push(
vscode.commands.registerCommand(
'ecooptimizer.openDiffEditor',
(originalFilePath: string, refactoredFilePath: string) => {
// Get the file name for the diff editor title
- const fileName = originalFilePath.split('/').pop() || 'file';
+ const fileName = path.basename(originalFilePath);
- // Show the diff editor
+ // 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,
- `${fileName} (original) ↔ ${fileName} (refactored)`,
+ `Refactoring Comparison (${fileName})`,
+ {
+ preview: false,
+ },
);
},
),
From ded38e75da88d9b4ffecac60843ecf5d48396eb3 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 04:06:42 -0400
Subject: [PATCH 107/215] added energy savings to tree items
---
src/commands/refactorSmell.ts | 5 +-
.../RefactoringDetailsViewProvider.ts | 98 ++++++++++---------
2 files changed, 55 insertions(+), 48 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 1947227..ffed663 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -30,10 +30,11 @@ export async function refactorSmell(
// Log the response from the backend
console.log('Refactoring response:', refactoredData);
- // Update the refactoring details view with the target file and affected files
+ // Update the refactoring details view with the target file, affected files, and energy saved
refactoringDetailsViewProvider.updateRefactoringDetails(
refactoredData.targetFile,
refactoredData.affectedFiles,
+ refactoredData.energySaved, // Pass the energy saved value
);
// Show a diff view for the target file
@@ -47,7 +48,7 @@ export async function refactorSmell(
refactoredUri,
`Refactoring Comparison (${fileName})`,
{
- preview: false,
+ preview: false, // Ensure the diff editor is not in preview mode
},
);
diff --git a/src/providers/RefactoringDetailsViewProvider.ts b/src/providers/RefactoringDetailsViewProvider.ts
index 432af54..0a464df 100644
--- a/src/providers/RefactoringDetailsViewProvider.ts
+++ b/src/providers/RefactoringDetailsViewProvider.ts
@@ -12,38 +12,46 @@ export class RefactoringDetailsViewProvider
private refactoringDetails: RefactoringDetailItem[] = [];
public targetFile: { original: string; refactored: string } | undefined;
public affectedFiles: { original: string; refactored: string }[] = [];
+ public energySaved: number | null = null; // Add energySaved as a class property
constructor() {
- // Initialize with an empty state
this.resetRefactoringDetails();
}
/**
- * Updates the refactoring details with the given target file and affected files.
+ * Updates the refactoring details with the given target file, affected files, and energy saved.
* @param targetFile - The target file (original and refactored paths).
* @param affectedFiles - The list of affected files (original and refactored paths).
+ * @param energySaved - The amount of energy saved in kg CO2.
*/
updateRefactoringDetails(
targetFile: { original: string; refactored: string },
affectedFiles: { original: string; refactored: string }[],
+ energySaved: number | null,
): void {
this.targetFile = targetFile;
this.affectedFiles = affectedFiles;
+ this.energySaved = energySaved;
- // Convert the absolute paths to relative paths for display
- const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
- const relativeTargetFile = workspaceFolder
- ? vscode.workspace.asRelativePath(targetFile.original)
- : targetFile.original;
+ // Clear the existing refactoring details
+ this.refactoringDetails = [];
- const relativeAffectedFiles = affectedFiles.map((file) =>
- workspaceFolder
- ? vscode.workspace.asRelativePath(file.original)
- : file.original,
- );
+ // Add energy saved as the first item
+ if (energySaved !== null) {
+ this.refactoringDetails.push(
+ new RefactoringDetailItem(
+ `Energy Saved: ${energySaved} kg CO2`, // Label
+ '', // No description
+ '', // No file path
+ '', // No file path
+ false, // Not collapsible
+ true, // Special item for energy saved
+ ),
+ );
+ }
- // Create the tree view items
- this.refactoringDetails = [
+ // Add the target file
+ this.refactoringDetails.push(
new RefactoringDetailItem(
path.basename(targetFile.original), // File name as label
'Target File', // Description
@@ -51,23 +59,10 @@ export class RefactoringDetailsViewProvider
targetFile.refactored,
true, // This is a parent item (collapsible)
),
- ];
+ );
- // Add affected files as child items
- if (affectedFiles.length > 0) {
- this.refactoringDetails.push(
- ...affectedFiles.map(
- (file) =>
- new RefactoringDetailItem(
- path.basename(file.original), // File name as label
- 'Affected File', // Description
- file.original,
- file.refactored,
- false, // This is a child item (not collapsible)
- ),
- ),
- );
- }
+ // Do not add affected files to refactoringDetails here
+ // They will be added dynamically in getChildren when the parent item is expanded
this._onDidChangeTreeData.fire(undefined); // Refresh the view
}
@@ -78,8 +73,7 @@ export class RefactoringDetailsViewProvider
resetRefactoringDetails(): void {
this.targetFile = undefined;
this.affectedFiles = [];
-
- // Clear the tree view
+ this.energySaved = null;
this.refactoringDetails = [];
this._onDidChangeTreeData.fire(undefined); // Refresh the view
}
@@ -105,32 +99,44 @@ export class RefactoringDetailsViewProvider
}
return []; // No nested items for child items
}
- // If no element is provided, return the parent item (Target File)
- return this.refactoringDetails.filter((item) => item.isParent);
+ // If no element is provided, return the top-level items (Energy Saved and Target File)
+ return this.refactoringDetails;
}
}
class RefactoringDetailItem extends vscode.TreeItem {
constructor(
- label: string, // File name
- description: string, // "Target File" or "Affected File"
+ label: string,
+ description: string,
public readonly originalFilePath: string,
public readonly refactoredFilePath: string,
- public readonly isParent: boolean = false, // Whether this is a parent item
+ public readonly isParent: boolean = false,
+ public readonly isEnergySaved: boolean = false,
) {
super(
label,
isParent
- ? vscode.TreeItemCollapsibleState.Collapsed // Parent item is collapsible
- : vscode.TreeItemCollapsibleState.None, // Child item is not collapsible
+ ? vscode.TreeItemCollapsibleState.Collapsed
+ : vscode.TreeItemCollapsibleState.None,
);
- this.description = description; // Set the description
+ this.description = description;
+
+ // Customize the icon for the Energy Saved item
+ if (isEnergySaved) {
+ this.iconPath = new vscode.ThemeIcon(
+ 'lightbulb', // Use a lightbulb icon for energy saved
+ new vscode.ThemeColor('charts.yellow'),
+ );
+ this.tooltip = 'This is the amount of energy saved by refactoring.';
+ }
- // Add a command to open the diff editor for both parent and child items
- this.command = {
- command: 'ecooptimizer.openDiffEditor',
- title: 'Open Diff Editor',
- arguments: [originalFilePath, refactoredFilePath],
- };
+ // Add a command to open the diff editor for file items (not energy saved)
+ if (!isEnergySaved) {
+ this.command = {
+ command: 'ecooptimizer.openDiffEditor',
+ title: 'Open Diff Editor',
+ arguments: [originalFilePath, refactoredFilePath],
+ };
+ }
}
}
From 44a84880145947d96ce60e7db1c5cc66641023c4 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 04:59:38 -0400
Subject: [PATCH 108/215] add to focus on view
---
src/commands/refactorSmell.ts | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index ffed663..3487d25 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -55,6 +55,9 @@ export async function refactorSmell(
// Set a context key to track that refactoring is in progress
vscode.commands.executeCommand('setContext', 'refactoringInProgress', true);
+ // Focus on the Refactoring Details view
+ await vscode.commands.executeCommand('ecooptimizer.refactoringDetails.focus');
+
// Notify the user
vscode.window.showInformationMessage(
`Refactoring successful! Energy saved: ${refactoredData.energySaved ?? 'N/A'} kg CO2`,
From 7d12294ce4c0e97ab0d9d448d244daf46bc209bb Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 05:16:05 -0400
Subject: [PATCH 109/215] added accept and reject buttons
---
src/commands/refactorSmell.ts | 62 +++++++++++++++++++++++++++++++
src/extension.ts | 70 +++++++++--------------------------
2 files changed, 80 insertions(+), 52 deletions(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 3487d25..1d7d16d 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -3,6 +3,7 @@ import { backendRefactorSmell } from '../api/backend';
import { SmellsViewProvider } from '../providers/SmellsViewProvider';
import { RefactoringDetailsViewProvider } from '../providers/RefactoringDetailsViewProvider';
import path from 'path';
+import * as fs from 'fs';
/**
* Handles the refactoring of a specific smell.
@@ -71,3 +72,64 @@ export async function refactorSmell(
vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
}
}
+
+/**
+ * Accepts the refactoring changes and saves the refactored files.
+ */
+export async function acceptRefactoring(
+ refactoringDetailsViewProvider: RefactoringDetailsViewProvider,
+) {
+ const targetFile = refactoringDetailsViewProvider.targetFile;
+ const affectedFiles = refactoringDetailsViewProvider.affectedFiles;
+
+ if (!targetFile || !affectedFiles) {
+ vscode.window.showErrorMessage('No refactoring data available.');
+ return;
+ }
+
+ try {
+ // Save the refactored target file
+ fs.copyFileSync(targetFile.refactored, targetFile.original);
+
+ // Save the refactored affected files
+ for (const file of affectedFiles) {
+ fs.copyFileSync(file.refactored, file.original);
+ }
+
+ // Notify the user
+ vscode.window.showInformationMessage('Refactoring accepted! Changes applied.');
+
+ // Reset the refactoring details view
+ refactoringDetailsViewProvider.resetRefactoringDetails();
+
+ // Close all diff editors
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+
+ // Set the context key to indicate refactoring is no longer in progress
+ vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
+ } catch (error) {
+ console.error('Failed to accept refactoring:', error);
+ vscode.window.showErrorMessage(
+ 'Failed to accept refactoring. Please try again.',
+ );
+ }
+}
+
+/**
+ * Rejects the refactoring changes and keeps the original files.
+ */
+export async function rejectRefactoring(
+ refactoringDetailsViewProvider: RefactoringDetailsViewProvider,
+) {
+ // Notify the user
+ vscode.window.showInformationMessage('Refactoring rejected! Changes discarded.');
+
+ // Reset the refactoring details view
+ refactoringDetailsViewProvider.resetRefactoringDetails();
+
+ // Close all diff editors
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+
+ // Set the context key to indicate refactoring is no longer in progress
+ vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
+}
diff --git a/src/extension.ts b/src/extension.ts
index 86fbcc9..9ee6690 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -15,7 +15,11 @@ import { checkServerStatus } from './api/backend';
import { FilterViewProvider } from './providers/FilterViewProvider';
import { SmellsCacheManager } from './context/SmellsCacheManager';
import { registerFileSaveListener } from './listeners/fileSaveListener';
-import { refactorSmell } from './commands/refactorSmell';
+import {
+ acceptRefactoring,
+ refactorSmell,
+ rejectRefactoring,
+} from './commands/refactorSmell';
import { RefactoringDetailsViewProvider } from './providers/RefactoringDetailsViewProvider';
import path from 'path';
@@ -138,57 +142,19 @@ export function activate(context: vscode.ExtensionContext): void {
}),
);
- // // Register the acceptRefactoring command
- // context.subscriptions.push(
- // vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', () => {
- // const refactoredFilePath = refactoringDetailsViewProvider.refactoredFilePath;
- // const originalFilePath = refactoringDetailsViewProvider.originalFilePath;
-
- // if (refactoredFilePath && originalFilePath) {
- // try {
- // // Replace the original file with the refactored file
- // fs.copyFileSync(refactoredFilePath, originalFilePath);
- // vscode.window.showInformationMessage(
- // 'Refactoring accepted! Changes applied.',
- // );
-
- // // Close the diff editor
- // vscode.commands.executeCommand('workbench.action.closeActiveEditor');
-
- // // Reset the refactoring details view
- // refactoringDetailsViewProvider.resetRefactoringDetails();
- // vscode.commands.executeCommand(
- // 'setContext',
- // 'refactoringInProgress',
- // false,
- // );
- // } catch (error) {
- // console.error('Failed to accept refactoring:', error);
- // vscode.window.showErrorMessage(
- // 'Failed to accept refactoring. Please try again.',
- // );
- // }
- // } else {
- // vscode.window.showErrorMessage('No refactoring data available.');
- // }
- // }),
- // );
-
- // // Register the rejectRefactoring command
- // context.subscriptions.push(
- // vscode.commands.registerCommand('ecooptimizer.rejectRefactoring', () => {
- // vscode.window.showInformationMessage(
- // 'Refactoring rejected! Changes discarded.',
- // );
-
- // // Close the diff editor
- // vscode.commands.executeCommand('workbench.action.closeActiveEditor');
-
- // // Reset the refactoring details view
- // refactoringDetailsViewProvider.resetRefactoringDetails();
- // vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
- // }),
- // );
+ // Register the acceptRefactoring command
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', () =>
+ acceptRefactoring(refactoringDetailsViewProvider),
+ ),
+ );
+
+ // Register the rejectRefactoring command
+ context.subscriptions.push(
+ vscode.commands.registerCommand('ecooptimizer.rejectRefactoring', () =>
+ rejectRefactoring(refactoringDetailsViewProvider),
+ ),
+ );
// Register the command to open the diff editor
context.subscriptions.push(
From d18460251f2792052537df06329b21140e7fe538 Mon Sep 17 00:00:00 2001
From: Nivetha Kuruparan
Date: Sat, 22 Mar 2025 12:20:25 -0400
Subject: [PATCH 110/215] make files outdated after refactoring
---
src/commands/refactorSmell.ts | 28 ++++++++++++++++++++++++++++
src/extension.ts | 6 +++++-
src/managers/SmellsViewUIManager.ts | 11 +++++++++++
3 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/src/commands/refactorSmell.ts b/src/commands/refactorSmell.ts
index 1d7d16d..b7c9506 100644
--- a/src/commands/refactorSmell.ts
+++ b/src/commands/refactorSmell.ts
@@ -2,9 +2,15 @@ import * as vscode from 'vscode';
import { backendRefactorSmell } from '../api/backend';
import { SmellsViewProvider } from '../providers/SmellsViewProvider';
import { RefactoringDetailsViewProvider } from '../providers/RefactoringDetailsViewProvider';
+import { SmellsCacheManager } from '../context/SmellsCacheManager';
import path from 'path';
import * as fs from 'fs';
+function normalizePath(filePath: string): string {
+ const normalizedPath = filePath.toLowerCase(); // Normalize case for consistent Map keying
+ return normalizedPath;
+}
+
/**
* Handles the refactoring of a specific smell.
*
@@ -75,9 +81,12 @@ export async function refactorSmell(
/**
* Accepts the refactoring changes and saves the refactored files.
+ * Marks the modified files as outdated and clears their smell cache.
*/
export async function acceptRefactoring(
refactoringDetailsViewProvider: RefactoringDetailsViewProvider,
+ smellsCacheManager: SmellsCacheManager,
+ smellsViewProvider: SmellsViewProvider,
) {
const targetFile = refactoringDetailsViewProvider.targetFile;
const affectedFiles = refactoringDetailsViewProvider.affectedFiles;
@@ -99,6 +108,22 @@ export async function acceptRefactoring(
// Notify the user
vscode.window.showInformationMessage('Refactoring accepted! Changes applied.');
+ // Clear the smell cache for the target file and affected files
+ await smellsCacheManager.clearCachedSmellsForFile(
+ normalizePath(targetFile.original),
+ );
+ for (const file of affectedFiles) {
+ await smellsCacheManager.clearCachedSmellsForFile(
+ normalizePath(file.original),
+ );
+ }
+
+ // Mark the target file and affected files as outdated
+ smellsViewProvider.markFileAsOutdated(normalizePath(targetFile.original));
+ for (const file of affectedFiles) {
+ smellsViewProvider.markFileAsOutdated(normalizePath(file.original));
+ }
+
// Reset the refactoring details view
refactoringDetailsViewProvider.resetRefactoringDetails();
@@ -107,6 +132,9 @@ export async function acceptRefactoring(
// Set the context key to indicate refactoring is no longer in progress
vscode.commands.executeCommand('setContext', 'refactoringInProgress', false);
+
+ // Refresh the UI to reflect the outdated status of the modified files
+ smellsViewProvider.refresh();
} catch (error) {
console.error('Failed to accept refactoring:', error);
vscode.window.showErrorMessage(
diff --git a/src/extension.ts b/src/extension.ts
index 9ee6690..76104c4 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -145,7 +145,11 @@ export function activate(context: vscode.ExtensionContext): void {
// Register the acceptRefactoring command
context.subscriptions.push(
vscode.commands.registerCommand('ecooptimizer.acceptRefactoring', () =>
- acceptRefactoring(refactoringDetailsViewProvider),
+ acceptRefactoring(
+ refactoringDetailsViewProvider,
+ smellsCacheManager,
+ smellsViewProvider,
+ ),
),
);
diff --git a/src/managers/SmellsViewUIManager.ts b/src/managers/SmellsViewUIManager.ts
index 1899d7e..e0dbb20 100644
--- a/src/managers/SmellsViewUIManager.ts
+++ b/src/managers/SmellsViewUIManager.ts
@@ -196,6 +196,13 @@ export class SmellsUIManager {
'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');
}
@@ -221,6 +228,10 @@ export class SmellsUIManager {
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';
}
From c4e22986eff9558861bcc3fcf1d0199954f319f1 Mon Sep 17 00:00:00 2001
From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com>
Date: Sat, 22 Mar 2025 17:26:27 -0400
Subject: [PATCH 111/215] Add energy metrics dashboard
fixes ssm-lab/capstone--source-code-optimizer#428
---
.env | 1 +
package.json | 20 ++
src/commands/detectSmells.ts | 6 +-
src/commands/exportMetricsData.ts | 58 +++++
src/commands/refactorSmell.ts | 14 +-
src/extension.ts | 31 ++-
src/providers/FilterViewProvider.ts | 8 +-
src/providers/MetricsViewProvider.ts | 326 +++++++++++++++++++++++++++
src/utils/envConfig.ts | 2 +
src/utils/smellsData.ts | 35 ++-
10 files changed, 486 insertions(+), 15 deletions(-)
create mode 100644 src/commands/exportMetricsData.ts
create mode 100644 src/providers/MetricsViewProvider.ts
diff --git a/.env b/.env
index f668a4c..779967a 100644
--- a/.env
+++ b/.env
@@ -1,3 +1,4 @@
SERVER_URL='127.0.0.1:8000'
FILE_HASH_CACHE_KEY='fileHashCache'
SMELL_CACHE_KEY='smellCache'
+WORKSPACE_METRICS_DATA='metricsData'
diff --git a/package.json b/package.json
index 0fa5dd9..9cf246a 100644
--- a/package.json
+++ b/package.json
@@ -142,6 +142,11 @@
"id": "ecooptimizer.refactoringDetails",
"name": "Refactoring Details",
"icon": "assets/eco-icon.png"
+ },
+ {
+ "id": "ecooptimizer.metricsView",
+ "name": "Carbon Metrics",
+ "icon": "assets/eco-icon.png"
}
]
},
@@ -150,6 +155,11 @@
"view": "ecooptimizer.view",
"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": [
@@ -189,6 +199,11 @@
"title": "Open File",
"category": "Eco Optimizer"
},
+ {
+ "command": "ecooptimizer.exportMetricsData",
+ "title": "Export Metrics Data as JSON",
+ "category": "Eco Optimizer"
+ },
{
"command": "ecooptimizer.detectSmellsFolder",
"title": "Detect Smells for All Files",
@@ -245,6 +260,11 @@
"command": "ecooptimizer.deselectAllFilterSmells",
"when": "view == ecooptimizer.filterView",
"group": "resource"
+ },
+ {
+ "command": "ecooptimizer.exportMetricsData",
+ "when": "view == ecooptimizer.metricsView",
+ "group": "resource"
}
],
"view/item/context": [
diff --git a/src/commands/detectSmells.ts b/src/commands/detectSmells.ts
index dbb4e0f..cc645d7 100644
--- a/src/commands/detectSmells.ts
+++ b/src/commands/detectSmells.ts
@@ -19,7 +19,7 @@ export async function detectSmellsFile(
smellsCacheManager: SmellsCacheManager,
treeDataProvider: SmellsViewProvider,
fileUri: vscode.Uri | string,
-) {
+): Promise {
// Validate the file URI or path
if (!fileUri) {
vscode.window.showErrorMessage('No file selected for analysis.');
@@ -125,7 +125,7 @@ export async function detectSmellsFolder(
smellsCacheManager: SmellsCacheManager,
treeDataProvider: SmellsViewProvider,
folderPath: string,
-) {
+): Promise {
// Notify the user that folder analysis has started
vscode.window.showInformationMessage(
`Detecting code smells for all Python files in: ${path.basename(folderPath)}`,
@@ -177,7 +177,7 @@ async function handleOutdatedFile(
filePath: string,
smellsCacheManager: SmellsCacheManager,
smellsDisplayProvider: SmellsViewProvider,
-) {
+): Promise {
// Check if the file is marked as outdated
if (smellsDisplayProvider.isFileOutdated(filePath)) {
// Delete cached smells for the outdated file
diff --git a/src/commands/exportMetricsData.ts b/src/commands/exportMetricsData.ts
new file mode 100644
index 0000000..3a1b91f
--- /dev/null
+++ b/src/commands/exportMetricsData.ts
@@ -0,0 +1,58 @@
+import * as vscode from 'vscode';
+import { dirname } from 'path';
+import { writeFileSync } from 'fs';
+
+import { MetricsDataItem } from '../providers/MetricsViewProvider';
+import { envConfig } from '../utils/envConfig';
+
+export async function exportMetricsData(
+ context: vscode.ExtensionContext,
+): Promise