-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a command to install eslint plugin #62
Merged
Merged
Changes from 15 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
4473a16
Added a new command.
sfdctaka 58c42fe
MVP.
sfdctaka a41a830
Prettier.
sfdctaka 370189c
Message to run npm install.
sfdctaka b3c55a4
Unit tests. Refactor.
sfdctaka 3542df1
Refactor.
sfdctaka d057115
More tests. More refactor.
sfdctaka a0a503d
Update after code review.
sfdctaka 50ec68e
Update after code review.
sfdctaka 7eb167a
Update src/commands/lint/configureLintingToolsCommand.ts
sfdctaka 083f070
Update src/commands/lint/configureLintingToolsCommand.ts
sfdctaka 80315e4
Update src/commands/lint/configureLintingToolsCommand.ts
sfdctaka 1cd2562
Update src/commands/lint/configureLintingToolsCommand.ts
sfdctaka e3f0d47
Update after code review.
sfdctaka a286f61
Fix config.
sfdctaka 13dea03
Update src/commands/lint/configureLintingToolsCommand.ts
sfdctaka e5fd8bb
Update src/commands/lint/configureLintingToolsCommand.ts
sfdctaka 9506c7d
Remove redundant import.
sfdctaka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,13 +35,41 @@ | |
"main": "./out/extension.js", | ||
"l10n": "./l10n", | ||
"contributes": { | ||
"menus": { | ||
"commandPalette": [ | ||
{ | ||
"command": "salesforcedx-vscode-offline-app.configureLintingTools", | ||
"when": "sfdx_project_opened" | ||
} | ||
] | ||
}, | ||
"commands": [ | ||
{ | ||
"command": "salesforcedx-vscode-offline-app.onboardingWizard", | ||
"title": "%extension.commands.config-wizard.title%", | ||
"category": "%extension.commands.config-wizard.category%" | ||
}, | ||
{ | ||
"command": "salesforcedx-vscode-offline-app.configureLintingTools", | ||
"title": "%extension.commands.config-linting-tools.title%", | ||
"category": "%extension.commands.salesforce-mobile-offline.category%" | ||
} | ||
], | ||
"configuration": { | ||
"title": "%salesforce.mobile.extensions%", | ||
"properties": { | ||
"mobileOfflineLinting.eslint-plugin-lwc-graph-analyzer": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
"type": "string", | ||
"default": "^0.9.0", | ||
"description": "%extension.commands.salesforce-mobile-offline.komaci.version%" | ||
}, | ||
"mobileOfflineLinting.eslint": { | ||
"type": "string", | ||
"default": "^8.47.0", | ||
"description": "%extension.commands.salesforce-mobile-offline.eslint.version%" | ||
} | ||
} | ||
] | ||
} | ||
}, | ||
"volta": { | ||
"node": "18.17.1" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,11 @@ | ||
{ | ||
"extension.commands.config-wizard.title": "Configuration Wizard", | ||
"extension.commands.config-wizard.category": "Offline Starter Kit", | ||
"extension.commands.config-linting-tools.title": "Configure Linting Tools", | ||
"extension.commands.salesforce-mobile-offline.category": "Salesforce Mobile Offline", | ||
"extension.commands.salesforce-mobile-offline.komaci.version": "Version of ESLint Plugin LWC Graph Analyzer to include in devDependencies", | ||
"extension.commands.salesforce-mobile-offline.eslint.version": "Version of ESLint to include in devDependencies", | ||
"salesforce.mobile.extensions": "Salesforce Mobile Extensions", | ||
"extension.displayName": "Salesforce Mobile Extensions for Visual Studio Code", | ||
"extension.description": "Tools to help developers create their Salesforce Mobile experiences in a VSCode development environment." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: MIT | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT | ||
*/ | ||
|
||
import { commands, l10n, window, workspace, ExtensionContext } from 'vscode'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import { WorkspaceUtils } from '../../utils/workspaceUtils'; | ||
import { JSON_INDENTATION_SPACES } from '../../utils/constants'; | ||
import * as vscode from 'vscode'; | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const config = vscode.workspace.getConfiguration(); | ||
const eslintPluginLwcGraphAnalyzer = | ||
'@salesforce/eslint-plugin-lwc-graph-analyzer'; | ||
const eslintPluginLwcGraphAnalyzerConfig = | ||
'mobileOfflineLinting.eslint-plugin-lwc-graph-analyzer'; | ||
const eslintPluginLwcGraphAnalyzerVersion = config.get( | ||
eslintPluginLwcGraphAnalyzerConfig | ||
) as string; | ||
|
||
const eslint = 'eslint'; | ||
const eslintConfig = 'mobileOfflineLinting.eslint'; | ||
const eslintVersion = config.get(eslintConfig) as string; | ||
|
||
const configureLintingToolsCommand = | ||
'salesforcedx-vscode-offline-app.configureLintingTools'; | ||
const eslintDependencies = [ | ||
[eslintPluginLwcGraphAnalyzer, eslintPluginLwcGraphAnalyzerVersion], | ||
[eslint, eslintVersion] | ||
]; | ||
|
||
const lwcGraphAnalyzerRecommended: string = | ||
'plugin:@salesforce/lwc-graph-analyzer/recommended'; | ||
const eslintRecommended = 'eslint:recommended'; | ||
|
||
interface PackageJson { | ||
devDependencies?: Record<string, string>; | ||
} | ||
|
||
enum MessageType { | ||
Error, | ||
InformationYesNo, | ||
InformationOk | ||
} | ||
|
||
export class ConfigureLintingToolsCommand { | ||
static async configure(): Promise<boolean> { | ||
try { | ||
if (!WorkspaceUtils.lwcFolderExists()) { | ||
await this.showMessage( | ||
'LWC folder does not exist. The folder is required to create a configuration file for eslint.' | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
); | ||
return Promise.resolve(false); | ||
} | ||
|
||
if (!WorkspaceUtils.packageJsonExists()) { | ||
await this.showMessage( | ||
'The sfdx project does not contain package.json. It is required to specify dependent eslint packages.' | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
); | ||
return Promise.resolve(false); | ||
} | ||
|
||
// Ask user to add eslint plugin | ||
const result = await this.showMessage( | ||
'Do you want to add the ESLint plugin for LWC graph analysis to your project? This will give you linting feedback on code patterns that will not support your LWCs working offline, for mobile use cases.', | ||
MessageType.InformationYesNo | ||
); | ||
|
||
if (!result || result.title === l10n.t('No')) { | ||
return Promise.resolve(false); | ||
} else { | ||
let modifiedDevDependencies = false; | ||
try { | ||
modifiedDevDependencies = this.updateDevDependencies(); | ||
} catch (error) { | ||
await this.showMessage( | ||
`Error updating package.json: ${error}` | ||
); | ||
return Promise.resolve(false); | ||
} | ||
|
||
let modifiedEslintrc = false; | ||
try { | ||
modifiedEslintrc = this.updateEslintrc(); | ||
} catch (error) { | ||
await this.showMessage( | ||
`Error updating .eslintrc.json: ${error}` | ||
); | ||
return Promise.resolve(false); | ||
} | ||
|
||
if (modifiedDevDependencies) { | ||
this.showMessage( | ||
`Updated package.json to include offline linting packages and dependencies.`, | ||
MessageType.InformationOk | ||
); | ||
} | ||
|
||
if (modifiedEslintrc) { | ||
this.showMessage( | ||
`Updated .eslintrc.json to include recommended linting rules.`, | ||
MessageType.InformationOk | ||
); | ||
} | ||
|
||
if (modifiedDevDependencies || modifiedEslintrc) { | ||
this.showMessage( | ||
`In the Terminal window, be sure to run the install command for your configured package manager, to install the updated dependencies. For example, "npm install" or "yarn install".`, | ||
MessageType.InformationOk | ||
); | ||
} | ||
|
||
if (!modifiedDevDependencies && !modifiedEslintrc) { | ||
this.showMessage( | ||
`All offline linting packages and dependencies are already configured in your project. No update has been made to package.json.`, | ||
MessageType.InformationOk | ||
); | ||
} | ||
|
||
return Promise.resolve(true); | ||
} | ||
} catch (error) { | ||
await this.showMessage( | ||
`There was an error trying to update either the offline linting dependencies or linting configuration: ${error}` | ||
); | ||
return Promise.resolve(false); | ||
} | ||
} | ||
|
||
static updateDevDependencies(): boolean { | ||
const packageJson: PackageJson = WorkspaceUtils.getPackageJson(); | ||
const devDependencies = packageJson.devDependencies; | ||
let modified = false; | ||
|
||
if (devDependencies) { | ||
eslintDependencies.forEach((nameValuePair) => { | ||
const [name, value] = nameValuePair; | ||
if (!devDependencies[name]) { | ||
devDependencies[name] = value; | ||
modified = true; | ||
} | ||
}); | ||
} | ||
|
||
if (modified) { | ||
// Save json only if the content was modified. | ||
WorkspaceUtils.setPackageJson(packageJson); | ||
} | ||
|
||
return modified; | ||
} | ||
|
||
static updateEslintrc(): boolean { | ||
const eslintrcPath = path.join( | ||
WorkspaceUtils.getWorkspaceDir(), | ||
WorkspaceUtils.LWC_PATH, | ||
'.eslintrc.json' | ||
); | ||
|
||
if (fs.existsSync(eslintrcPath)) { | ||
const eslintrc = JSON.parse(fs.readFileSync(eslintrcPath, 'utf-8')); | ||
|
||
if (!eslintrc.extends) { | ||
eslintrc.extends = []; | ||
} | ||
|
||
const eslintrcExtends = eslintrc.extends as Array<string>; | ||
|
||
let modified = false; | ||
|
||
if (!eslintrcExtends.includes(eslintRecommended)) { | ||
eslintrcExtends.push(eslintRecommended); | ||
modified = true; | ||
} | ||
|
||
if (!eslintrcExtends.includes(lwcGraphAnalyzerRecommended)) { | ||
eslintrc.extends.push(lwcGraphAnalyzerRecommended); | ||
modified = true; | ||
} | ||
|
||
if (modified) { | ||
// Save json only if the content was modified. | ||
fs.writeFileSync( | ||
eslintrcPath, | ||
JSON.stringify(eslintrc, null, JSON_INDENTATION_SPACES) | ||
); | ||
} | ||
|
||
return modified; | ||
} else { | ||
// Create eslintrc | ||
const eslintrc = { | ||
extends: [ | ||
`${eslintRecommended}`, | ||
`${lwcGraphAnalyzerRecommended}` | ||
] | ||
}; | ||
const jsonString = JSON.stringify( | ||
eslintrc, | ||
null, | ||
JSON_INDENTATION_SPACES | ||
); | ||
|
||
fs.writeFileSync(eslintrcPath, jsonString); | ||
|
||
return true; | ||
} | ||
} | ||
|
||
static async showMessage( | ||
message: string, | ||
messageType: MessageType = MessageType.Error | ||
): Promise<{ title: string } | undefined> { | ||
const localizedMessage = l10n.t(message); | ||
switch (messageType) { | ||
case MessageType.Error: | ||
return await window.showErrorMessage(localizedMessage, { | ||
title: l10n.t('OK') | ||
}); | ||
case MessageType.InformationYesNo: | ||
return await window.showInformationMessage( | ||
localizedMessage, | ||
{ title: l10n.t('Yes') }, | ||
{ title: l10n.t('No') } | ||
); | ||
case MessageType.InformationOk: | ||
return await window.showInformationMessage(localizedMessage, { | ||
title: l10n.t('OK') | ||
}); | ||
} | ||
} | ||
} | ||
|
||
export function registerCommand(context: ExtensionContext) { | ||
commands.registerCommand(configureLintingToolsCommand, async () => { | ||
await ConfigureLintingToolsCommand.configure(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh cool, I didn't know about this functionality. That's a clean way to specify a precondition here.