diff --git a/.eslintrc.json b/.eslintrc.json index 707762b..2caf91a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -20,7 +20,6 @@ "no-dupe-keys": "error", "no-duplicate-case": "error", "no-duplicate-imports": "error", - "no-unused-vars": "error", "curly": "error", "eqeqeq": "error", "no-throw-literal": "warn", diff --git a/.github/workflows/release-local.yml b/.github/workflows/release-local.yml index b344be9..0c833cc 100644 --- a/.github/workflows/release-local.yml +++ b/.github/workflows/release-local.yml @@ -21,7 +21,7 @@ jobs: path: vscode-viva - name: Install the dependencies - run: npm install + run: npm ci working-directory: vscode-viva - name: Checkout cli-microsoft365 @@ -36,7 +36,7 @@ jobs: dir - name: Restore dependencies for cli-microsoft365 - run: npm install + run: npm i working-directory: cli-microsoft365 - name: Build cli-microsoft365 diff --git a/package-lock.json b/npm-shrinkwrap.json similarity index 99% rename from package-lock.json rename to npm-shrinkwrap.json index 8f4653b..61dfd93 100644 --- a/package-lock.json +++ b/npm-shrinkwrap.json @@ -57,7 +57,7 @@ "webpack-dev-server": "^4.8.1" }, "engines": { - "vscode": "^1.65.0" + "vscode": "^1.90.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -3828,19 +3828,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "extraneous": true }, - "node_modules/@pnp/cli-microsoft365/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "extraneous": true, - "hasInstallScript": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/@pnp/cli-microsoft365/node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -13140,7 +13127,7 @@ } }, "node_modules/npm/node_modules/ip": { - "version": "2.0.0", + "version": "2.0.1", "dev": true, "inBundle": true, "license": "MIT" @@ -14277,7 +14264,7 @@ "inBundle": true, "license": "MIT", "dependencies": { - "ip": "^2.0.0", + "ip": "^2.0.1", "smart-buffer": "^4.2.0" }, "engines": { diff --git a/src/constants/ComponentTypes.ts b/src/constants/ComponentTypes.ts index 1256f07..87ba79f 100644 --- a/src/constants/ComponentTypes.ts +++ b/src/constants/ComponentTypes.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars */ // eslint-disable-next-line no-shadow export enum ComponentType { adaptiveCardExtension = 'adaptiveCardExtension', diff --git a/src/constants/ExtensionTypes.ts b/src/constants/ExtensionTypes.ts index c9360fb..429bc54 100644 --- a/src/constants/ExtensionTypes.ts +++ b/src/constants/ExtensionTypes.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars */ // eslint-disable-next-line no-shadow export enum ExtensionType { application = 'ApplicationCustomizer', diff --git a/src/constants/FrameworkTypes.ts b/src/constants/FrameworkTypes.ts index 8a17f55..b1fa20e 100644 --- a/src/constants/FrameworkTypes.ts +++ b/src/constants/FrameworkTypes.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars */ // eslint-disable-next-line no-shadow export enum FrameworkType { none = 'none', diff --git a/src/constants/NodeVersionManagers.ts b/src/constants/NodeVersionManagers.ts index 65b0c49..c4209f7 100644 --- a/src/constants/NodeVersionManagers.ts +++ b/src/constants/NodeVersionManagers.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars */ // eslint-disable-next-line no-shadow export enum NodeVersionManagers { nvm = 'nvm', diff --git a/src/constants/ProjectFileContent.ts b/src/constants/ProjectFileContent.ts index 0f3461a..ba5a945 100644 --- a/src/constants/ProjectFileContent.ts +++ b/src/constants/ProjectFileContent.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars */ // eslint-disable-next-line no-shadow export enum ProjectFileContent { init = 'init-project', diff --git a/src/constants/WebViewTypes.ts b/src/constants/WebViewTypes.ts index f657bfd..c64d83c 100644 --- a/src/constants/WebViewTypes.ts +++ b/src/constants/WebViewTypes.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars */ // eslint-disable-next-line no-shadow export enum WebViewType { samplesGallery = 'samplesGallery', diff --git a/src/constants/WorkflowTypes.ts b/src/constants/WorkflowTypes.ts index 9dc1e2c..c07e6cd 100644 --- a/src/constants/WorkflowTypes.ts +++ b/src/constants/WorkflowTypes.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars */ // eslint-disable-next-line no-shadow export enum WorkflowType { gitHub = 'GitHub', diff --git a/src/providers/AuthProvider.ts b/src/providers/AuthProvider.ts index 1999a0c..ad66c80 100644 --- a/src/providers/AuthProvider.ts +++ b/src/providers/AuthProvider.ts @@ -19,7 +19,6 @@ export class M365AuthenticationSession implements AuthenticationSession { // Required for the session, but not for M365 CLI public readonly accessToken: string = ''; - // eslint-disable-next-line no-unused-vars constructor(public readonly account: AuthenticationSessionAccountInformation) { } } @@ -31,7 +30,7 @@ export class AuthProvider implements AuthenticationProvider, Disposable { private initializedDisposable: Disposable | undefined; /** - * Register the authentication provider + * Registers the authentication provider and associated commands. */ public static register() { const ext = Extension.getInstance(); @@ -56,60 +55,59 @@ export class AuthProvider implements AuthenticationProvider, Disposable { } /** - * Returns the auth instance - * @returns + * Returns the singleton instance of the AuthProvider class. + * @returns The singleton instance of the AuthProvider class. */ public static getInstance(): AuthProvider { return AuthProvider.instance; } /** - * Verify if the user is logged in + * Verifies the authentication status. + * Calls the `login` method of the `AuthProvider` class with `false` as the argument. */ public static verify() { AuthProvider.login(false); } /** - * Login to M365 - * @param createIfNone - * @returns + * Logs in the user. + * @param createIfNone - A boolean indicating whether to create a new session if none exists. */ public static async login(createIfNone: boolean = true) { await authentication.getSession(AuthProvider.id, [], { createIfNone }); } /** - * Logout from M365 + * Logs out the user by removing the session. */ public static async logout() { AuthProvider.instance.removeSession(''); } /** - * Event emitter for session changes + * Event that fires when the authentication sessions change. */ public get onDidChangeSessions(): Event { return this.onDidChangeEventEmit.event; } /** - * Get the current session - * @param scopes - * @returns + * Retrieves the authentication sessions for the specified scopes. + * If no scopes are provided, retrieves all authentication sessions. + * @param scopes - The scopes for which to retrieve authentication sessions. + * @returns A promise that resolves to an array of authentication sessions. */ - // eslint-disable-next-line no-unused-vars public async getSessions(scopes?: readonly string[]): Promise { const account = await this.getAccount(); return account ? [account] : []; } /** - * Create a new session - * @param _scopes - * @returns + * Creates a session for authentication. + * @param _scopes - The scopes for the session. + * @returns A promise that resolves to an AuthenticationSession. */ - // eslint-disable-next-line no-unused-vars public async createSession(_scopes: string[]): Promise { return new Promise((resolve) => { window.withProgress({ @@ -157,11 +155,10 @@ export class AuthProvider implements AuthenticationProvider, Disposable { } /** - * Remove a session - * @param _sessionId - * @returns + * Removes a session with the specified session ID. + * @param _sessionId - The ID of the session to remove. + * @returns A Promise that resolves when the session is successfully removed. */ - // eslint-disable-next-line no-unused-vars public async removeSession(_sessionId: string): Promise { const output = await executeCommand('logout', { output: 'text' }); @@ -183,8 +180,12 @@ export class AuthProvider implements AuthenticationProvider, Disposable { } /** - * Get the account that is currently signed in - * @returns + * Retrieves the M365 authentication session for the current account. + * If the account is not available, it tries to fetch the account information using the 'status' command. + * If successful, it returns a new M365AuthenticationSession object with the account details. + * If unsuccessful, it logs an error message and returns undefined. + * If the account is already available, it returns a new M365AuthenticationSession object with the account details. + * @returns A Promise that resolves to an M365AuthenticationSession object or undefined. */ public async getAccount(): Promise { if (!EnvironmentInformation.account) { diff --git a/src/services/AdaptiveCardCheck.ts b/src/services/AdaptiveCardCheck.ts index db7a374..877d053 100644 --- a/src/services/AdaptiveCardCheck.ts +++ b/src/services/AdaptiveCardCheck.ts @@ -5,8 +5,11 @@ import { parseYoRc } from '../utils/parseYoRc'; export class AdaptiveCardCheck { + /** - * Check if yo-rc has ACE component + * Validates the ACE (Adaptive Card Extension) component. + * If the required extension is not installed, prompts the user to install it. + * @returns A promise that resolves when the validation is complete. */ public static async validateACEComponent() { try { diff --git a/src/services/CertificateActions.ts b/src/services/CertificateActions.ts index 60106f8..6f347eb 100644 --- a/src/services/CertificateActions.ts +++ b/src/services/CertificateActions.ts @@ -7,6 +7,11 @@ import { Uri, workspace } from 'vscode'; export class CertificateActions { + /** + * Generates a certificate and saves it as a PFX file in the workspace. + * @param certPassword - The password to protect the generated certificate. + * @returns A base64-encoded string representation of the generated PFX file. + */ public static async generateCertificate(certPassword: string): Promise { try { const keys = pki.rsa.generateKeyPair(2048); diff --git a/src/services/CliActions.ts b/src/services/CliActions.ts index 8b7608a..2258b8c 100644 --- a/src/services/CliActions.ts +++ b/src/services/CliActions.ts @@ -46,6 +46,10 @@ export class CliActions { ); } + /** + * Retrieves the URLs of the app catalogs in the environment. + * @returns A promise that resolves to an array of app catalog URLs, or undefined if no app catalogs are found. + */ public static async appCatalogUrlsGet(): Promise { const appCatalogUrls: string[] = []; const tenantAppCatalog = (await CliExecuter.execute('spo tenant appcatalogurl get', 'json')).stdout || undefined; @@ -64,6 +68,12 @@ export class CliActions { return EnvironmentInformation.appCatalogUrls; } + /** + * Retrieves the tenant-wide extensions from the specified tenant app catalog URL. + * @param tenantAppCatalogUrl The URL of the tenant app catalog. + * @returns A promise that resolves to an array of objects containing the URL and title of each tenant-wide extension, + * or undefined if no extensions are found. + */ public static async getTenantWideExtensions(tenantAppCatalogUrl: string): Promise<{Url: string, Title: string}[] | undefined> { const origin = new URL(tenantAppCatalogUrl).origin; const commandOptions: any = { @@ -86,6 +96,11 @@ export class CliActions { return tenantWideExtensionList; } + /** + * Retrieves the health information of the tenant services. + * @returns A promise that resolves to an array of objects containing the title and URL of the health information. + * Returns undefined if there is no health information available. + */ public static async getTenantHealthInfo(): Promise<{Title: string, Url: string}[] | undefined> { const healthInfo = (await CliExecuter.execute('tenant serviceannouncement health list', 'json')).stdout || undefined; @@ -103,6 +118,11 @@ export class CliActions { return healthInfoList; } + /** + * Generates a workflow form based on the provided input. + * @param input - The input for generating the workflow form. + * @returns A Promise that resolves when the workflow form generation is complete. + */ public static async generateWorkflowForm(input: GenerateWorkflowCommandInput) { // Change the current working directory to the root of the Project const wsFolder = await Folders.getWorkspaceFolder(); @@ -132,7 +152,6 @@ export class CliActions { location: ProgressLocation.Notification, title: 'Creating app registration...', cancellable: true - // eslint-disable-next-line no-unused-vars }, async (progress: Progress<{ message?: string; increment?: number }>) => { try { const commandOptions: any = {}; @@ -171,7 +190,6 @@ export class CliActions { location: ProgressLocation.Notification, title: `Generating ${input.workflowType === WorkflowType.gitHub ? 'GitHub Workflow' : 'Azure DevOps Pipeline'}...`, cancellable: true - // eslint-disable-next-line no-unused-vars }, async (progress: Progress<{ message?: string; increment?: number }>) => { try { const commandOptions: any = {}; @@ -224,6 +242,10 @@ export class CliActions { }); } + /** + * Upgrades the project by generating the upgrade steps and displaying them in a Markdown preview. + * @private + */ private static async upgrade() { // Change the current working directory to the root of the Project const wsFolder = await Folders.getWorkspaceFolder(); @@ -241,7 +263,6 @@ export class CliActions { location: ProgressLocation.Notification, title: 'Generating the upgrade steps...', cancellable: true - // eslint-disable-next-line no-unused-vars }, async (progress: Progress<{ message?: string; increment?: number }>) => { try { const result = await CliExecuter.execute('spfx project upgrade', 'md'); @@ -267,6 +288,10 @@ export class CliActions { }); } + /** + * Renames the current project. + * @returns A promise that resolves when the project is renamed. + */ private static async renameProject() { // Change the current working directory to the root of the Project const wsFolder = await Folders.getWorkspaceFolder(); @@ -309,7 +334,6 @@ export class CliActions { location: ProgressLocation.Notification, title: 'Renaming the current project...', cancellable: true - // eslint-disable-next-line no-unused-vars }, async (progress: Progress<{ message?: string; increment?: number }>) => { try { let result: CommandOutput; @@ -329,6 +353,14 @@ export class CliActions { }); } + /** + * Grants API permissions for the current project. + * This method changes the current working directory to the root of the project, + * and then executes the command to grant API permissions. + * If the project is a Teams Toolkit project, the source directory is set to 'src'. + * Displays progress notifications during the execution. + * @returns A promise that resolves when the API permissions are granted. + */ private static async grantAPIPermissions() { // Change the current working directory to the root of the Project const wsFolder = await Folders.getWorkspaceFolder(); @@ -346,7 +378,6 @@ export class CliActions { location: ProgressLocation.Notification, title: 'Granting API permissions for the current project...', cancellable: true - // eslint-disable-next-line no-unused-vars }, async (progress: Progress<{ message?: string; increment?: number }>) => { try { await CliExecuter.execute('spfx project permissions grant', 'json'); @@ -363,6 +394,11 @@ export class CliActions { }); } + /** + * Displays the generate workflow form in a PnPWebview. + * Retrieves the necessary data and opens the webview with the data. + * @returns A promise that resolves when the form is displayed. + */ private static async showGenerateWorkflowForm() { const content = await parseYoRc(); const data = { @@ -373,6 +409,13 @@ export class CliActions { PnPWebview.open(WebViewType.workflowForm, data); } + /** + * Validates the current project. + * This method changes the current working directory to the root of the project and performs + * validation on the project. If the project is a Teams Toolkit project, it changes the working + * directory to the 'src' folder before performing validation. + * @returns A promise that resolves when the validation is complete. + */ private static async validateProject() { // Change the current working directory to the root of the Project const wsFolder = await Folders.getWorkspaceFolder(); @@ -390,7 +433,6 @@ export class CliActions { location: ProgressLocation.Notification, title: 'Validating the current project...', cancellable: true - // eslint-disable-next-line no-unused-vars }, async (progress: Progress<{ message?: string; increment?: number }>) => { try { const result = await CliExecuter.execute('spfx project doctor', 'md'); @@ -416,6 +458,10 @@ export class CliActions { }); } + /** + * Deploys a project. + * @param file The file to deploy. If not provided, the method will search for .sppkg files in the workspace and prompt the user to select one. + */ private static async deploy(file: Uri | undefined) { const authInstance = AuthProvider.getInstance(); const account = await authInstance.getAccount(); @@ -481,7 +527,6 @@ export class CliActions { location: ProgressLocation.Notification, title: `Deploying the ${basename(file.fsPath)} project. Check [output window](command:${Commands.showOutputChannel}) to follow the progress.`, cancellable: false - // eslint-disable-next-line no-unused-vars }, async (progress: Progress<{ message?: string; increment?: number }>) => { try { const addResult = await CliExecuter.execute('spo app add', 'json', { filePath: file?.fsPath, appCatalogUrl: appCatalogUrl, appCatalogScope: appCatalogScope, overwrite: true }); @@ -521,6 +566,10 @@ export class CliActions { }); } + /** + * Serves the project by executing the specified configuration using Gulp. + * Prompts the user to select a configuration from the serve.json file. + */ public static async serveProject() { const wsFolder = Folders.getWorkspaceFolder(); if (!wsFolder) { diff --git a/src/services/CliCommandExecuter.ts b/src/services/CliCommandExecuter.ts index 1273ac1..5a550b7 100644 --- a/src/services/CliCommandExecuter.ts +++ b/src/services/CliCommandExecuter.ts @@ -5,22 +5,24 @@ import { CommandOutput, executeCommand } from '@pnp/cli-microsoft365'; export class CliExecuter { /** - * Execute the command - * @param command - * @param args - * @returns + * Executes a CLI command and returns the command output. + * @param command - The CLI command to execute. + * @param output - The desired output format of the command. Defaults to 'text'. + * @param args - Additional arguments to pass to the command. + * @returns A promise that resolves to the command output. */ public static async execute(command: string, output: string | undefined = 'text', args?: any): Promise { return await CliExecuter.tryExecuteCommand(command, output, args); } /** - * Try to execute the command and log the output to the output channel. - * @param command - * @returns + * Executes a CLI command asynchronously and returns the command output. + * @param command The CLI command to execute. + * @param output The type of output to expect from the command (default: 'text'). + * @param args Additional arguments to pass to the command. + * @returns A Promise that resolves to the command output. */ private static async tryExecuteCommand(command: string, output: string | undefined = 'text', args?: any): Promise { - // eslint-disable-next-line no-unused-vars return await new Promise((resolve: (res: CommandOutput) => void, reject: (e: Error) => void): void => { Logger.getInstance(); let cmdOutput: string = ''; diff --git a/src/services/CommandExecuter.ts b/src/services/CommandExecuter.ts index 2d2f7c4..901e6ec 100644 --- a/src/services/CommandExecuter.ts +++ b/src/services/CommandExecuter.ts @@ -7,11 +7,11 @@ import { Logger } from './Logger'; export class Executer { /** - * Execute the command - * @param workingDirectory - * @param command - * @param args - * @returns + * Executes a command in the specified working directory with optional arguments. + * @param workingDirectory The working directory in which to execute the command. + * @param command The command to execute. + * @param args Optional arguments to pass to the command. + * @returns A promise that resolves to the exit code of the command. */ public static async executeCommand(workingDirectory: string, command: string, args: string[] = []): Promise { const result: CommandResult = await Executer.tryExecuteCommand(workingDirectory, command, ...args); @@ -19,14 +19,13 @@ export class Executer { } /** - * Try to execute the command and log the output to the output channel. - * @param workingDirectory - * @param command - * @param args - * @returns + * Executes a command asynchronously and returns a promise that resolves to a CommandResult. + * @param workingDirectory - The working directory for the command execution. + * @param command - The command to execute. + * @param args - The arguments to pass to the command. + * @returns A promise that resolves to a CommandResult. */ private static async tryExecuteCommand(workingDirectory: string | undefined, command: string, ...args: string[]): Promise { - // eslint-disable-next-line no-unused-vars return await new Promise((resolve: (res: CommandResult) => void, reject: (e: Error) => void): void => { Logger.getInstance(); let cmdOutput: string = ''; diff --git a/src/services/DebuggerCheck.ts b/src/services/DebuggerCheck.ts index 4fce999..a29ed3e 100644 --- a/src/services/DebuggerCheck.ts +++ b/src/services/DebuggerCheck.ts @@ -11,8 +11,9 @@ export class DebuggerCheck { private static servePlaceholderUrl = 'https://contoso.sharepoint.com'; /** - * Check if the URL is used in the launch.json file - * @param url + * Validates the provided URL by checking the launch.json and serve.json files. + * If any errors occur during validation, they will be logged using the Logger. + * @param url - The URL to validate. */ public static async validateUrl(url: string) { try { @@ -29,9 +30,8 @@ export class DebuggerCheck { } /** - * Validate the launch.json file - * @param url - * @returns + * Validates the launch configuration and updates the placeholder URL with the provided URL. + * @param url The URL to update the placeholder URL with. */ private static async validateLaunch(url: string) { let launchFiles = await workspace.findFiles('.vscode/launch.json', '**/node_modules/**'); @@ -74,9 +74,10 @@ export class DebuggerCheck { } /** - * Validate the serve.json file - * @param url - * @returns + * Validates the serve configuration by checking if the initialPage and serveConfigurations + * use placeholder URLs and prompts the user to update them with the provided URL. + * If the user chooses to update, the URLs are modified and the serve configuration file is saved. + * @param url - The URL to be used as a replacement for the placeholder URLs. */ private static async validateServe(url: string) { let serveFiles = await workspace.findFiles('config/serve.json', '**/node_modules/**'); diff --git a/src/services/Dependencies.ts b/src/services/Dependencies.ts index 7fa5770..8d36ee1 100644 --- a/src/services/Dependencies.ts +++ b/src/services/Dependencies.ts @@ -25,7 +25,9 @@ export class Dependencies { } /** - * Validate if all the required dependencies are installed + * Validates the dependencies required for SPFx development. + * Checks the Node.js version and npm dependencies. + * Displays notifications for missing or incompatible dependencies. */ public static async validate() { await window.withProgress({ @@ -90,7 +92,7 @@ export class Dependencies { } /** - * Install all the dependencies + * Installs the dependencies by running the npm install command in a terminal. */ public static install() { const terminal = window.createTerminal({ @@ -105,7 +107,8 @@ export class Dependencies { } /** - * Check node version + * Checks if the installed version of Node.js is valid. + * @returns Returns true if the installed version of Node.js is valid, otherwise false. */ private static isValidNodeJs() { try { @@ -145,7 +148,11 @@ export class Dependencies { } /** - * split dependency into name and version + * Splits a dependency string into an array of strings. + * If the dependency starts with '@', it splits the string into two parts: the scope and the package name. + * If the dependency does not start with '@', it splits the string into two parts: the package name and the version. + * @param dependency - The dependency string to split. + * @returns An array of strings containing the split parts of the dependency. */ private static splitDependency(dependency: string): string[] { if (dependency.startsWith('@')) { diff --git a/src/services/Extension.ts b/src/services/Extension.ts index 810cd2a..b87bc74 100644 --- a/src/services/Extension.ts +++ b/src/services/Extension.ts @@ -4,12 +4,13 @@ import { ExtensionContext, ExtensionMode, SecretStorage } from 'vscode'; export class Extension { private static instance: Extension; - // eslint-disable-next-line no-unused-vars private constructor(private ctx: ExtensionContext) {} /** - * Creates the singleton instance for the extension. - * @param ctx + * Gets the instance of the Extension class. + * If an instance doesn't exist, it creates a new one using the provided ExtensionContext. + * @param ctx - The ExtensionContext object. + * @returns The instance of the Extension class. */ public static getInstance(ctx?: ExtensionContext): Extension { if (!Extension.instance && ctx) { @@ -20,49 +21,56 @@ export class Extension { } /** - * Get the name of the extension + * Gets the name of the extension. + * @returns The name of the extension. */ public get name(): string { return this.ctx.extension.packageJSON.name; } /** - * Get the display name of the extension + * Gets the display name of the extension. + * @returns The display name as a string. */ public get displayName(): string { return this.ctx.extension.packageJSON.displayName; } /** - * Returns the extension's version + * Gets the version of the extension. + * @returns The version string. */ public get version(): string { return this.ctx.extension.packageJSON.version; } /** - * Check if the extension is in production/development mode + * Determines whether the extension is running in production mode. + * @returns True if the extension is running in production mode, false otherwise. */ public get isProductionMode(): boolean { return this.ctx.extensionMode === ExtensionMode.Production; } /** - * Get the extension's subscriptions + * Returns the subscriptions of the Extension. + * @returns An array of disposable objects representing the subscriptions. */ public get subscriptions(): { dispose(): any; }[] { return this.ctx.subscriptions; } /** - * Get the extension's secrets + * Gets the secret storage. + * @returns The secret storage. */ public get secrets(): SecretStorage { return this.ctx.secrets; } /** - * Get the extension's path + * Gets the path of the extension. + * @returns The path of the extension. */ public get extensionPath(): string { return this.ctx.extensionPath; diff --git a/src/services/Folders.ts b/src/services/Folders.ts index d469a84..6f61ddc 100644 --- a/src/services/Folders.ts +++ b/src/services/Folders.ts @@ -4,8 +4,9 @@ import { workspace, window, WorkspaceFolder } from 'vscode'; export class Folders { /** - * Retrieve the workspace folder - */ + * Retrieves the workspace folder. + * @returns A promise that resolves to the workspace folder, or undefined if there are no workspace folders or the user cancels the selection. + */ public static async getWorkspaceFolder(): Promise { let folder: WorkspaceFolder | undefined; diff --git a/src/services/Scaffolder.ts b/src/services/Scaffolder.ts index 26fa759..97ef84e 100644 --- a/src/services/Scaffolder.ts +++ b/src/services/Scaffolder.ts @@ -34,14 +34,27 @@ export class Scaffolder { ); } + /** + * Creates a project using the provided input. + * @param input - The input for the project creation. + */ public static async createProject(input: SpfxScaffoldCommandInput) { Scaffolder.scaffold(input, true); } + /** + * Adds a component to the project. + * @param input - The input for the SpfxAddComponentCommand. + */ public static async addComponentToProject(input: SpfxAddComponentCommandInput) { Scaffolder.scaffold(input, false); } + /** + * Uses the provided sample to create a new project. + * @param sample - The sample to use for creating the project. + * @returns A Promise that resolves when the project creation is complete. + */ public static async useSample(sample: Sample) { Logger.info(`Start using sample ${sample.name}`); @@ -92,6 +105,9 @@ export class Scaffolder { }); } + /** + * Displays a dialog to pick a folder and sends the selected folder path to the webview. + */ public static async pickFolder() { const folder = await window.showOpenDialog({ canSelectFolders: true, @@ -105,6 +121,11 @@ export class Scaffolder { } } + /** + * Validates the solution name by checking if a folder with the same name already exists. + * @param folderPath - The path of the folder where the solution will be created. + * @param solutionNameInput - The input solution name to be validated. + */ public static validateSolutionName(folderPath: string, solutionNameInput: string) { if (existsSync(join(folderPath, solutionNameInput))) { PnPWebview.postMessage(WebviewCommand.toWebview.validateSolutionName, false); @@ -114,6 +135,11 @@ export class Scaffolder { PnPWebview.postMessage(WebviewCommand.toWebview.validateSolutionName, true); } + /** + * Validates the component name for a given component type. + * @param componentType - The type of the component. + * @param componentNameInput - The input component name to validate. + */ public static async validateComponentName(componentType: ComponentType, componentNameInput: string) { if (await Scaffolder.componentFolderExists(componentType, componentNameInput)) { PnPWebview.postMessage(WebviewCommand.toWebview.validateComponentName, false); @@ -123,6 +149,12 @@ export class Scaffolder { PnPWebview.postMessage(WebviewCommand.toWebview.validateComponentName, true); } + /** + * Scaffold method for creating a new project. + * @param input - The input for the scaffold command. + * @param isNewProject - A boolean indicating whether it's a new project or not. + * @returns A Promise that resolves when the scaffold process is complete. + */ private static async scaffold(input: SpfxScaffoldCommandInput | SpfxAddComponentCommandInput, isNewProject: boolean) { Logger.info('Start creating a new project'); @@ -208,6 +240,10 @@ export class Scaffolder { }); } + /** + * Retrieves the folder path where the project will be created. + * @returns A Promise that resolves to the selected folder path, or undefined if no folder is selected. + */ private static async getFolderPath(): Promise { const wsFolder = await Folders.getWorkspaceFolder(); const folderOptions: QuickPickItem[] = [{ @@ -248,6 +284,10 @@ export class Scaffolder { return folderPath; } + /** + * Displays the create project form in a webview. + * @returns A promise that resolves when the form is displayed. + */ private static async showCreateProjectForm() { PnPWebview.open(WebViewType.scaffoldForm, { isNewProject: true, @@ -255,6 +295,10 @@ export class Scaffolder { }); } + /** + * Displays the add project form in a PnPWebview. + * @returns A promise that resolves when the form is displayed. + */ private static async showAddProjectForm() { PnPWebview.open(WebViewType.scaffoldForm, { isNewProject: false, @@ -262,6 +306,10 @@ export class Scaffolder { }); } + /** + * Retrieves the version of Node.js installed on the system. + * @returns The version of Node.js as a string. + */ private static getNodeVersion(): string { const output = execSync('node --version', { shell: TerminalCommandExecuter.shell }); const match = /v(?\d+)\.(?\d+)\.(?\d+)/gm.exec(output.toString()); @@ -269,6 +317,11 @@ export class Scaffolder { return nodeVersion; } + /** + * Creates a project file with the given content and opens the folder in Visual Studio Code. + * @param folderPath - The path of the folder where the project file will be created. + * @param content - The content of the project file. + */ private static async createProjectFileAndOpen(folderPath: string, content: any) { if (content) { writeFileSync(join(folderPath, PROJECT_FILE), content, { encoding: 'utf8' }); @@ -281,6 +334,11 @@ export class Scaffolder { } } + /** + * Retrieves the solution name from the user. + * @param folderPath - The path of the folder where the solution will be created. + * @returns A promise that resolves to the solution name entered by the user, or undefined if no solution name is provided. + */ private static async getSolutionName(folderPath: string): Promise { return await window.showInputBox({ title: 'What is your solution name?', @@ -301,6 +359,12 @@ export class Scaffolder { }); } + /** + * Checks if a component folder exists in the workspace. + * @param type - The type of the component. + * @param value - The value of the component. + * @returns A boolean indicating whether the component folder exists. + */ private static async componentFolderExists(type: ComponentType, value: string) { let componentFolder = ''; switch (type) { diff --git a/src/services/TerminalCommandExecuter.ts b/src/services/TerminalCommandExecuter.ts index 9687945..287642b 100644 --- a/src/services/TerminalCommandExecuter.ts +++ b/src/services/TerminalCommandExecuter.ts @@ -22,10 +22,20 @@ export class TerminalCommandExecuter { TerminalCommandExecuter.initShellPath(); } + /** + * Gets the shell path. + * @returns The shell path. + */ public static get shell() { return TerminalCommandExecuter.shellPath; } + /** + * Initializes the shell path for executing terminal commands. + * If the shell path is an object with a `path` property, it sets the `shellPath` to that value. + * If the shell path is a string, it sets the `shellPath` to that value. + * If the shell path is undefined, it sets the `shellPath` to undefined. + */ private static initShellPath() { const shell: string | { path: string } | undefined = TerminalCommandExecuter.getShellPath(); @@ -37,8 +47,8 @@ export class TerminalCommandExecuter { } /** - * Retrieve the automation profile for the current platform - * @returns + * Retrieves the shell path or shell setting based on the current platform. + * @returns The shell path or shell setting for the current platform, or undefined if not found. */ private static getShellPath(): string | ShellSetting | undefined { const platform = getPlatform(); @@ -59,12 +69,25 @@ export class TerminalCommandExecuter { return terminalSettings.get(`integrated.shell.${platform}`); } + /** + * Registers the commands for execution. + * @param subscriptions - The array of subscriptions to add the registered command to. + */ private static registerCommands(subscriptions: Subscription[]) { subscriptions.push( commands.registerCommand(Commands.executeTerminalCommand, TerminalCommandExecuter.runCommand) ); } + /** + * Creates a new terminal with the specified name and icon. + * If a terminal with the same name already exists, it returns that terminal instead. + * If the user's settings specify to use a specific node version manager (nvm or nvs), + * it checks for the presence of .nvmrc files and sets the appropriate node version manager command. + * @param name - The name of the terminal. + * @param icon - The path to the icon for the terminal. + * @returns A promise that resolves to the created terminal or the existing terminal with the same name. + */ private static async createTerminal(name?: string, icon?: string): Promise { let terminal = window.terminals.find(t => t.name === name); @@ -94,10 +117,22 @@ export class TerminalCommandExecuter { return terminal; } + /** + * Retrieves the extension settings value for the specified setting. + * If the setting is not found, the default value is returned. + * @param setting - The name of the setting to retrieve. + * @param defaultValue - The default value to return if the setting is not found. + * @returns The value of the setting, or the default value if the setting is not found. + */ private static getExtensionSettings(setting: string, defaultValue: T): T { return workspace.getConfiguration(EXTENSION_NAME).get(setting, defaultValue); } + /** + * Runs a command in the specified terminal. + * @param command - The command to run. + * @param terminal - The terminal in which to run the command. + */ private static async runInTerminal(command: string, terminal?: Terminal | undefined) { if (terminal) { terminal.show(true); @@ -105,7 +140,11 @@ export class TerminalCommandExecuter { } } - // eslint-disable-next-line no-unused-vars + /** + * Runs a command in the terminal. + * @param command - The command to run. + * @param args - The arguments for the command. + */ public static async runCommand(command: string, args: string[]) { const terminal = await TerminalCommandExecuter.createTerminal('Gulp task', 'tasks-list-configure'); diff --git a/src/webview/view/components/App.tsx b/src/webview/view/components/App.tsx index ffafe68..daf0da9 100644 --- a/src/webview/view/components/App.tsx +++ b/src/webview/view/components/App.tsx @@ -15,7 +15,6 @@ export interface IAppProps { data: any | null } -// eslint-disable-next-line no-unused-vars export const App: React.FunctionComponent = ({ url, data }: React.PropsWithChildren) => { const navigate = useNavigate(); diff --git a/src/webview/view/components/controls/MultiSelect.tsx b/src/webview/view/components/controls/MultiSelect.tsx index 129d2d9..ad908d1 100644 --- a/src/webview/view/components/controls/MultiSelect.tsx +++ b/src/webview/view/components/controls/MultiSelect.tsx @@ -4,7 +4,6 @@ import { ChevronDownIcon } from '../icons/ChevronDownIcon'; export interface IMultiSelectProps { - // eslint-disable-next-line no-unused-vars onChange: (event: any, option?: IDropdownOption) => void; options: IDropdownOption[]; label?: string; diff --git a/src/webview/view/components/forms/spfxProject/AdditionalStep.tsx b/src/webview/view/components/forms/spfxProject/AdditionalStep.tsx index eedb236..9c001cc 100644 --- a/src/webview/view/components/forms/spfxProject/AdditionalStep.tsx +++ b/src/webview/view/components/forms/spfxProject/AdditionalStep.tsx @@ -6,16 +6,12 @@ import { PackageSelector } from './PackageSelector'; interface AdditionalStepProps { shouldRunInit: boolean; - // eslint-disable-next-line no-unused-vars setShouldRunInit: (value: boolean) => void; shouldInstallReusablePropertyPaneControls: boolean; - // eslint-disable-next-line no-unused-vars setShouldInstallReusablePropertyPaneControls: (value: boolean) => void; shouldInstallReusableReactControls: boolean; - // eslint-disable-next-line no-unused-vars setShouldInstallReusableReactControls: (value: boolean) => void; shouldInstallPnPJs: boolean; - // eslint-disable-next-line no-unused-vars setShouldInstallPnPJs: (value: boolean) => void; } diff --git a/src/webview/view/components/forms/spfxProject/ComponentDetailsStep.tsx b/src/webview/view/components/forms/spfxProject/ComponentDetailsStep.tsx index ce72b1c..d27d279 100644 --- a/src/webview/view/components/forms/spfxProject/ComponentDetailsStep.tsx +++ b/src/webview/view/components/forms/spfxProject/ComponentDetailsStep.tsx @@ -11,19 +11,14 @@ interface IComponentDetailsStepProps { componentType: ComponentType; componentName: string; isValidComponentName: boolean | null | undefined; - // eslint-disable-next-line no-unused-vars setComponentName: (name: string) => void; - // eslint-disable-next-line no-unused-vars setIsValidComponentName: (value: boolean | null) => void; nodeVersion: string; frameworkType: string; - // eslint-disable-next-line no-unused-vars setFrameworkType: (type: string) => void; extensionType: ExtensionType; - // eslint-disable-next-line no-unused-vars setExtensionType: (type: ExtensionType) => void; aceType: string; - // eslint-disable-next-line no-unused-vars setAceType: (type: string) => void; } diff --git a/src/webview/view/components/forms/spfxProject/GeneralInfoStep.tsx b/src/webview/view/components/forms/spfxProject/GeneralInfoStep.tsx index 3e7fe9f..bb7881f 100644 --- a/src/webview/view/components/forms/spfxProject/GeneralInfoStep.tsx +++ b/src/webview/view/components/forms/spfxProject/GeneralInfoStep.tsx @@ -10,15 +10,11 @@ import { Messenger } from '@estruyf/vscode/dist/client'; export interface IGeneralInfoProps { isNewProject: boolean; folderPath: string; - // eslint-disable-next-line no-unused-vars setFolderPath: (folderPath: string) => void; solutionName: string; - // eslint-disable-next-line no-unused-vars setSolutionName: (value: string) => void; isValidSolutionName: boolean | null | undefined; - // eslint-disable-next-line no-unused-vars setIsValidSolutionName: (value: boolean | null) => void; - // eslint-disable-next-line no-unused-vars setComponentType: (componentType: ComponentType) => void; componentTypes: { value: string; name: string }[]; } diff --git a/src/webview/view/components/forms/spfxProject/PackageSelector.tsx b/src/webview/view/components/forms/spfxProject/PackageSelector.tsx index 7760f01..8e007fa 100644 --- a/src/webview/view/components/forms/spfxProject/PackageSelector.tsx +++ b/src/webview/view/components/forms/spfxProject/PackageSelector.tsx @@ -4,7 +4,6 @@ import { VSCodeButton, VSCodeLink } from '@vscode/webview-ui-toolkit/react'; interface PackageSelectorProps { value: boolean; - // eslint-disable-next-line no-unused-vars setValue: (value: boolean) => void, label: string, link?: string diff --git a/src/webview/view/components/gallery/SearchBar.tsx b/src/webview/view/components/gallery/SearchBar.tsx index 7bde3af..6a7617d 100644 --- a/src/webview/view/components/gallery/SearchBar.tsx +++ b/src/webview/view/components/gallery/SearchBar.tsx @@ -7,13 +7,9 @@ import { MultiSelect } from '../controls'; export interface ISearchBarProps { - // eslint-disable-next-line no-unused-vars onSearchTextboxChange: (event: any) => void; - // eslint-disable-next-line no-unused-vars onFilterBySPFxVersionChange: (event: any, option?: IDropdownOption) => void; - // eslint-disable-next-line no-unused-vars onFilterByComponentTypeChange: (event: any, option?: IDropdownOption) => void; - // eslint-disable-next-line no-unused-vars onFilterOnlyScenariosChange: (event: any) => void; initialQuery?: string; spfxVersions: IDropdownOption[]; diff --git a/src/webview/view/hooks/useSamples.tsx b/src/webview/view/hooks/useSamples.tsx index 9725e27..2aee059 100644 --- a/src/webview/view/hooks/useSamples.tsx +++ b/src/webview/view/hooks/useSamples.tsx @@ -6,7 +6,6 @@ import { Sample } from '../../../models'; const SAMPLES_URL = 'https://raw.githubusercontent.com/pnp/vscode-viva/main/data/sp-dev-fx-samples.json'; -// eslint-disable-next-line no-unused-vars export default function useSamples(): [Sample[], string[], ((query: string, componentTypes: string[], spfxVersions: string[], showOnlyScenarios: boolean) => void)] { const [allSamples, setAllSamples] = useState(undefined); const [samples, setSamples] = useState(undefined);