Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 83 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions packages/code-analyzer-eslint-engine/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@salesforce/code-analyzer-eslint-engine",
"description": "Plugin package that adds 'eslint' as an engine into Salesforce Code Analyzer",
"version": "0.29.0",
"version": "0.29.0-SNAPSHOT",
"author": "The Salesforce Code Analyzer Team",
"license": "BSD-3-Clause",
"homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview",
Expand All @@ -16,10 +16,11 @@
"@eslint/js": "^9.32.0",
"@lwc/eslint-plugin-lwc": "^3.2.0",
"@lwc/eslint-plugin-lwc-platform": "^6.1.0",
"@salesforce/eslint-config-lwc": "^4.0.0",
"@salesforce/eslint-plugin-lightning": "^2.0.0",
"@salesforce-ux/eslint-plugin-slds": "^0.5.0",
"@salesforce/code-analyzer-engine-api": "0.27.0",
"@salesforce/code-analyzer-eslint8-engine": "0.5.0",
"@salesforce/eslint-config-lwc": "^4.0.0",
"@salesforce/eslint-plugin-lightning": "^2.0.0",
"@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^8.39.0",
"@typescript-eslint/parser": "^8.33.1",
Expand Down
15 changes: 15 additions & 0 deletions packages/code-analyzer-eslint-engine/src/base-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import eslintJs from "@eslint/js";
import eslintTs from "typescript-eslint";
import lwcEslintPluginLwcPlatform from "@lwc/eslint-plugin-lwc-platform";
import salesforceEslintConfigLwc from "@salesforce/eslint-config-lwc";
import sldsEslintPlugin from "@salesforce-ux/eslint-plugin-slds";
import {ESLintEngineConfig} from "./config";
import globals from "globals";

Expand Down Expand Up @@ -44,6 +45,9 @@ export class BaseConfigFactory {
} else if (this.useLwcBaseConfig()) {
configArray.push(...this.createLwcConfigArray());
}
if (this.useSldsBaseConfig()) {
configArray.push(...this.createSldsConfigArray());
}
if (this.useTsBaseConfig()) {
configArray.push(...this.createTypescriptConfigArray());
}
Expand Down Expand Up @@ -105,6 +109,13 @@ export class BaseConfigFactory {
}];
}

private createSldsConfigArray(): Linter.Config[] {
return sldsEslintPlugin.configs['flat/recommended'].map(conf => ({
...conf,
files: this.engineConfig.file_extensions.html.map(ext => `**/*${ext}`)
}));
}

private createTypescriptConfigArray(): Linter.Config[] {
const configs: Linter.Config[] = [];
for (const conf of ([eslintJs.configs.all, ...eslintTs.configs.all] as Linter.Config[])) {
Expand Down Expand Up @@ -136,6 +147,10 @@ export class BaseConfigFactory {
return !this.engineConfig.disable_lwc_base_config && this.engineConfig.file_extensions.javascript.length > 0;
}

private useSldsBaseConfig(): boolean {
return !this.engineConfig.disable_slds_base_config && this.engineConfig.file_extensions.html.length > 0;
}

private useTsBaseConfig(): boolean {
return !this.engineConfig.disable_typescript_base_config && this.engineConfig.file_extensions.typescript.length > 0;
}
Expand Down
22 changes: 18 additions & 4 deletions packages/code-analyzer-eslint-engine/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,21 @@ export type ESLintEngineConfig = {
// Default: false
disable_lwc_base_config: boolean

// If true then the base configuration that supplies the slds rules for html and cmp files will not be applied.
// Default: false
disable_slds_base_config: boolean

// If true then the base configuration that supplies the standard rules for typescript files will not be applied.
// Default: false
disable_typescript_base_config: boolean

// Extensions of the files in your workspace that will be used to discover rules.
// To associate file extensions to the standard ESLint JavaScript rules, LWC rules, or custom JavaScript-based
// rules, add them under the 'javascript' language. To associate file extensions to the standard TypeScript
// rules or custom TypeScript-based rules, add them under the 'typescript' language. To allow for the
// discovery of custom rules that are associated with any other language, then add the associated file
// extensions under the 'other' language.
// rules or custom TypeScript-based rules, add them under the 'typescript' language. To associate file extensions
// to standard LWC HTML rules, Component (CMP) rules, or custom HTML rules, add them under the 'html' language.
// To allow for the discovery of custom rules that are associated with any other language, then add the associated
// file extensions under the 'other' language.
file_extensions: FileExtensionsObject

// (INTERNAL USE ONLY) Copy of the code analyzer config root.
Expand All @@ -47,6 +52,7 @@ export type ESLintEngineConfig = {
export type FileExtensionsObject = {
javascript: string[],
typescript: string[],
html: string[],
other: string[]
};

Expand All @@ -56,10 +62,12 @@ export const DEFAULT_CONFIG: ESLintEngineConfig = {
auto_discover_eslint_config: false,
disable_javascript_base_config: false,
disable_lwc_base_config: false,
disable_slds_base_config: false,
disable_typescript_base_config: false,
file_extensions: {
javascript: ['.js', '.cjs', '.mjs'],
typescript: ['.ts'],
html: ['.html', '.htm', '.cmp'],
other: []
},
config_root: process.cwd() // INTERNAL USE ONLY
Expand Down Expand Up @@ -93,6 +101,11 @@ export const ESLINT_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = {
valueType: "boolean",
defaultValue: DEFAULT_CONFIG.disable_lwc_base_config
},
disable_slds_base_config: {
descriptionText: getMessage('ConfigFieldDescription_disable_slds_base_config'),
valueType: "boolean",
defaultValue: DEFAULT_CONFIG.disable_slds_base_config
},
disable_typescript_base_config: {
descriptionText: getMessage('ConfigFieldDescription_disable_typescript_base_config'),
valueType: "boolean",
Expand Down Expand Up @@ -122,7 +135,7 @@ export const LEGACY_ESLINT_IGNORE_FILE: string = '.eslintignore';
export function validateAndNormalizeConfig(configValueExtractor: ConfigValueExtractor): ESLintEngineConfig {
configValueExtractor.validateContainsOnlySpecifiedKeys(['eslint_config_file', 'eslint_ignore_file',
'auto_discover_eslint_config', 'disable_javascript_base_config', 'disable_lwc_base_config',
'disable_typescript_base_config', 'file_extensions']);
'disable_slds_base_config', 'disable_typescript_base_config', 'file_extensions']);

const eslintConfigValueExtractor: ESLintEngineConfigValueExtractor = new ESLintEngineConfigValueExtractor(configValueExtractor);
return {
Expand All @@ -132,6 +145,7 @@ export function validateAndNormalizeConfig(configValueExtractor: ConfigValueExtr
auto_discover_eslint_config: eslintConfigValueExtractor.extractBooleanValue('auto_discover_eslint_config'),
disable_javascript_base_config: eslintConfigValueExtractor.extractBooleanValue('disable_javascript_base_config'),
disable_lwc_base_config: eslintConfigValueExtractor.extractBooleanValue('disable_lwc_base_config'),
disable_slds_base_config: eslintConfigValueExtractor.extractBooleanValue('disable_slds_base_config'),
disable_typescript_base_config: eslintConfigValueExtractor.extractBooleanValue('disable_typescript_base_config'),
file_extensions: eslintConfigValueExtractor.extractFileExtensionsValue(),
};
Expand Down
12 changes: 9 additions & 3 deletions packages/code-analyzer-eslint-engine/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ const MESSAGE_CATALOG : { [key: string]: string } = {
`and "plugin:@lwc/lwc-platform/recommended" configurations to Code Analyzer.\n` +
`See https://github.com/salesforce/eslint-config-lwc and https://www.npmjs.com/package/@lwc/eslint-plugin-lwc-platform.`,

ConfigFieldDescription_disable_slds_base_config:
`Whether to turn off the default base configuration that supplies the SLDS rules for Lightning Web Components and Aura Components.\n` +
`The base configuration for SLDS adds the rules from the "plugin:@salesforce-ux/eslint-plugin-slds/recommended" configuration to Code Analyzer.\n` +
`See https://www.npmjs.com/package/@salesforce-ux/eslint-plugin-slds`,

ConfigFieldDescription_disable_typescript_base_config:
`Whether to turn off the default base configuration that supplies the standard rules for TypeScript files\n` +
`The base configuration for TypeScript files adds the rules from the "plugin:@typescript-eslint:all" configuration to Code Analyzer.\n` +
Expand All @@ -44,9 +49,10 @@ const MESSAGE_CATALOG : { [key: string]: string } = {
`Extensions of the files in your workspace that will be used to discover rules.\n` +
`To associate file extensions to the standard ESLint JavaScript rules, LWC rules, or custom JavaScript-based\n` +
`rules, add them under the 'javascript' language. To associate file extensions to the standard TypeScript\n` +
`rules or custom TypeScript-based rules, add them under the 'typescript' language. To allow for the\n` +
`discovery of custom rules that are associated with any other language, then add the associated file\n` +
`extensions under the 'other' language.`,
`rules or custom TypeScript-based rules, add them under the 'typescript' language.\n` +
`To associate file extensions to standard LWC HTML rules, Component (CMP) rules, or custom HTML rules, add them\n` +
`under the 'html' language. To allow for the discovery of custom rules that are associated with any other language,\n` +
`then add the associated file extensions under the 'other' language.`,

UnsupportedEngineName:
`The ESLintEnginePlugin doesn't support an engine with name '%s'.`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,18 @@ declare module '@salesforce/eslint-config-lwc' {
};
export = moduleObject;
}

// This declaration adds in the missing types for "@salesforce-ux/eslint-plugin-slds" whose package.json file's main field points to:
// node_modules/@salesforce-ux/eslint-plugin-slds/build/index.js
declare module '@salesforce-ux/eslint-plugin-slds' {
import type { ESLint, Linter } from 'eslint';
import type { RuleDefinition } from "@eslint/core";

const plugin: ESLint.ObjectMetaProperties & {
readonly rules: Record<string, RuleDefinition>;
readonly configs: {
readonly "flat/recommended": Linter.Config[];
};
};
export = plugin;
}
16 changes: 16 additions & 0 deletions packages/code-analyzer-eslint-engine/src/rule-mappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1595,5 +1595,21 @@ export const RULE_MAPPINGS: Record<string, {severity: SeverityLevel, tags: strin
"@typescript-eslint/use-unknown-in-catch-callback-variable": {
severity: SeverityLevel.Moderate,
tags: [/* NOT RECOMMENDED */ COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.TYPESCRIPT]
},

// =================================================================================================================
// SLDS/HTML BASE RULES - @salesforce-ux/eslint-plugin-slds
// =================================================================================================================
"@salesforce-ux/slds/enforce-bem-usage": {
severity: SeverityLevel.Low,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.HTML]
},
"@salesforce-ux/slds/modal-close-button-issue": {
severity: SeverityLevel.Moderate,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.HTML]
},
"@salesforce-ux/slds/no-deprecated-classes-slds2": {
severity: SeverityLevel.High,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.ERROR_PRONE, COMMON_TAGS.LANGUAGES.HTML]
}
}
2 changes: 2 additions & 0 deletions packages/code-analyzer-eslint-engine/src/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class SpecifiedESLintWorkspace extends ESLintWorkspace {
const relevantFileExtensions: string[] = [
...this.fileExts.javascript,
...this.fileExts.typescript,
...this.fileExts.html,
...this.fileExts.other];
this.cachedPreIgnoredFilesToScan = (await this.workspace.getTargetedFiles())
.filter(file => relevantFileExtensions.includes(path.extname(file).toLowerCase()));
Expand Down Expand Up @@ -126,6 +127,7 @@ class UnspecifiedESLintWorkspace extends ESLintWorkspace {
const relevantFileExtensions: string[] = [
...this.fileExts.javascript,
...this.fileExts.typescript,
...this.fileExts.html,
...this.fileExts.other];
return relevantFileExtensions.map(ext => `${baseDir}${path.sep}placeholderCandidateFile${ext}`);
}
Expand Down
7 changes: 7 additions & 0 deletions packages/code-analyzer-eslint-engine/test/end-to-end.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ describe('End to end test', () => {
'@typescript-eslint/no-unused-vars', // there are 4 of these
'no-invalid-regexp'
]));
const violationsFromHTMLFile: Violation[] = engineRunResults.violations.filter(v => path.extname(v.codeLocations[0].file) === '.html');
expect(violationsFromHTMLFile).toHaveLength(1);
expect(violationsFromHTMLFile[0].ruleName).toEqual('@salesforce-ux/slds/enforce-bem-usage');

const warnLogs: LogEvent[] = logEvents.filter(e => e.logLevel == LogLevel.Warn);
expect(warnLogs).toHaveLength(0);
Expand Down Expand Up @@ -109,6 +112,10 @@ describe('End to end test', () => {
'no-invalid-regexp'
]));

// SLDS violations are only relevant for v9+
const violationsFromHTMLFile: Violation[] = engineRunResults.violations.filter(v => path.extname(v.codeLocations[0].file) === '.html');
expect(violationsFromHTMLFile).toHaveLength(0);

const warnLogs: LogEvent[] = logEvents.filter(e => e.logLevel == LogLevel.Warn);
expect(warnLogs).toHaveLength(1);
expect(warnLogs[0].message).toContain('Using ESLint v8 instead of ESLint v9');
Expand Down
Loading
Loading