-
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
Changes from 8 commits
4473a16
58c42fe
a41a830
370189c
b3c55a4
3542df1
d057115
a0a503d
50ec68e
7eb167a
083f070
80315e4
1cd2562
e3f0d47
a286f61
13dea03
e5fd8bb
9506c7d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
{ | ||
"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.displayName": "Salesforce Mobile Extensions for Visual Studio Code", | ||
"extension.description": "Tools to help developers create their Salesforce Mobile experiences in a VSCode development environment." | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
/* | ||
* 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, ExtensionContext } from 'vscode'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import { WorkspaceUtils } from '../../utils/workspaceUtils'; | ||
import { TAB_SPACES } from '../../utils/constants'; | ||
|
||
const configureLintingToolsCommand = | ||
'salesforcedx-vscode-offline-app.configureLintingTools'; | ||
const eslintDependencies = [ | ||
['@salesforce/eslint-plugin-lwc-graph-analyzer', '^0.9.0'], | ||
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. These are configuration items where at least the version values would be better represented in contributes.configuration settings. That way a) we take hard-coded data out of the code, and b) users can update these values in their own personal configuration settings, to override the versions we select. 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. |
||
['eslint', '^8.47.0'] | ||
]; | ||
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 function onActivate(context: ExtensionContext) { | ||
commands.executeCommand( | ||
'setContext', | ||
'sfdx_project_opened', | ||
WorkspaceUtils.isSfdxProjectOpened() | ||
); | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
export class ConfigureLintingToolsCommand { | ||
static async configure(): Promise<boolean> { | ||
try { | ||
if (!WorkspaceUtils.lwcFolderExists()) { | ||
await this.showMessage('LWC folder does not exist.'); | ||
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. Do these messages need any further context? I don't know what the message title/header will look like, if any. But on their own, these messages seem a little sparse/ambiguous. |
||
return Promise.resolve(false); | ||
} | ||
|
||
if (!WorkspaceUtils.packageJsonExists()) { | ||
await this.showMessage( | ||
'The project does not contain package.json.' | ||
); | ||
return Promise.resolve(false); | ||
} | ||
|
||
// Ask user to add eslint plugin | ||
const result = await this.showMessage( | ||
'Do you want to add eslint plugin for LWC data graph anaylsis to your package.json?', | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
MessageType.InformationYesNo | ||
); | ||
|
||
if (!result || result.title === l10n.t('No')) { | ||
return Promise.resolve(false); | ||
} else { | ||
let modified = false; | ||
try { | ||
modified = this.updateDevDependencies(); | ||
} catch (error) { | ||
await this.showMessage( | ||
`Error updating package.json: ${error}` | ||
); | ||
return Promise.resolve(false); | ||
} | ||
|
||
try { | ||
this.updateEslintrc(); | ||
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. We should be tracking whether this file was updated or not too, and reporting to the user accordingly—even if the report is built into a single message. We could be updating this but not the 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. |
||
} catch (error) { | ||
await this.showMessage( | ||
`Error updating .eslintrc.json: ${error}` | ||
); | ||
return Promise.resolve(false); | ||
} | ||
|
||
if (modified) { | ||
await this.showMessage( | ||
`Updated developer dependency in package.json. Run package manager such as npmr/yarn/pnpm to update node modules.`, | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
MessageType.InformationOk | ||
); | ||
} else { | ||
await this.showMessage( | ||
`No update was made in package.json. It already includes eslint plugin for LWC data graph analysis.`, | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
MessageType.InformationOk | ||
); | ||
} | ||
|
||
return Promise.resolve(true); | ||
} | ||
} catch (error) { | ||
await this.showMessage( | ||
`There was an error trying to update developer dependency in package.json: ${error}` | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
); | ||
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() { | ||
const eslintrcPath = path.join( | ||
WorkspaceUtils.getWorkspaceDir(), | ||
WorkspaceUtils.LWC_PATH, | ||
'.eslintrc.json' | ||
); | ||
|
||
if (fs.existsSync(eslintrcPath)) { | ||
const eslintrc = JSON.parse(fs.readFileSync(eslintrcPath, 'utf-8')); | ||
const eslintrcExtends = eslintrc.extends as Array<string>; | ||
|
||
if (eslintrc && eslintrcExtends) { | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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, TAB_SPACES) | ||
); | ||
} | ||
} | ||
} else { | ||
// Create eslintrc | ||
fs.writeFileSync( | ||
eslintrcPath, | ||
`{"extends": ["${eslintRecommended}", "${lwcGraphAnalyzerRecommended}"]}` | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
); | ||
} | ||
} | ||
|
||
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 await window.showInformationMessage( | ||
sfdctaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
localizedMessage, | ||
{ | ||
title: l10n.t('OK') | ||
} | ||
); | ||
} | ||
} | ||
} | ||
|
||
export function registerCommand(context: ExtensionContext) { | ||
commands.registerCommand(configureLintingToolsCommand, async () => { | ||
await ConfigureLintingToolsCommand.configure(); | ||
}); | ||
} |
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.