Skip to content
Closed
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
73 changes: 68 additions & 5 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion packages/code-analyzer-eslint-engine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"@eslint/js": "^9.32.0",
"@lwc/eslint-plugin-lwc": "^3.2.0",
"@lwc/eslint-plugin-lwc-platform": "^6.1.0",
"@salesforce-ux/eslint-plugin-slds": "^0.5.0",
"@salesforce-ux/eslint-plugin-slds": "1.0.0-internal-alpha.0",
"@salesforce/code-analyzer-engine-api": "0.29.0-SNAPSHOT",
"@salesforce/code-analyzer-eslint8-engine": "0.6.0-SNAPSHOT",
"@salesforce/eslint-config-lwc": "^4.0.0",
Expand Down
139 changes: 109 additions & 30 deletions packages/code-analyzer-eslint-engine/src/base-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import globals from "globals";

export class BaseConfigFactory {
private readonly engineConfig: ESLintEngineConfig;

constructor(engineConfig: ESLintEngineConfig) {
this.engineConfig = engineConfig;
}

createBaseConfigArray(): Linter.Config[] {
const configArray: Linter.Config[] = [{
linterOptions: {
Expand All @@ -28,7 +28,7 @@ export class BaseConfigFactory {
"$Label": "readonly", // ^
"$Locale": "readonly", // ^
"$Resource": "readonly", // ^

// ESLint doesn't natively know about various browser and node globals. So we add them here to
// remove false positives for our users.
... globals.node,
Expand All @@ -37,85 +37,160 @@ export class BaseConfigFactory {
}
}
}];

if (this.useJsBaseConfig() && this.useLwcBaseConfig()) {
configArray.push(...this.createJavascriptPlusLwcConfigArray());
} else if (this.useJsBaseConfig()) {
configArray.push(...this.createJavascriptConfigArray());
} else if (this.useLwcBaseConfig()) {
configArray.push(...this.createLwcConfigArray());
}
if (this.useSldsBaseConfig()) {
configArray.push(...this.createSldsConfigArray());
if (this.useSldsCSSBaseConfig()) {
configArray.push(...this.createSldsCSSConfigArray());
}
if (this.useSldsHTMLBaseConfig()) {
configArray.push(...this.createSldsHTMLConfigArray());
}
if (this.useTsBaseConfig()) {
configArray.push(...this.createTypescriptConfigArray());
}
return configArray;
}

private createJavascriptPlusLwcConfigArray(): Linter.Config[] {
let configs: Linter.Config[] = validateAndGetRawLwcConfigArray();

// TODO: Remove the For the following 2 updates when https://github.com/salesforce/eslint-config-lwc/issues/158 is fixed
// 1) Turn off the babel parser's configFile option from the lwc base plugin
configs[0].languageOptions!.parserOptions!.babelOptions.configFile = false;
// 2) For some reason babel doesn't like .cjs files unless we explicitly set this to undefined because I think
// ESLint 9 is setting it to "commonjs" automatically when the field doesn't exist in the parserOptions (and for
// babel "commonjs" isn't a valid option)
configs[0].languageOptions!.parserOptions!.sourceType = undefined;

// Swap out eslintJs.configs.recommended with eslintJs.configs.all
configs[1] = eslintJs.configs.all;

// This one rule is broken and thus, we need to turn it off for now.
// See https://git.soma.salesforce.com/lwc/eslint-plugin-lwc-platform/issues/152
configs[5].rules = {
...configs[5].rules,
'@lwc/lwc-platform/valid-offline-wire': 'off'
}

// Restrict these configs to just javascript files
configs = configs.map(config => {
return {
...config,
files: this.engineConfig.file_extensions.javascript.map(ext => `**/*${ext}`)
}
});

return configs;
}

private createLwcConfigArray(): Linter.Config[] {
const configs: Linter.Config[] = this.createJavascriptPlusLwcConfigArray();

// Remove any explicitly listed rule that is a base javascript rule from the recommended LWC/Lightning rules.
// Note the modified base rules don't have namespace like jest/*, @lwc/*, etc (and thus has no '/').
configs[4].rules = Object.fromEntries(
Object.entries(configs[4].rules as Linter.RulesRecord).filter(([key]) => key.includes('/'))
);

// Remove the eslintJs.configs.all (at element 1). Note that this delete is after the configs[4] update above so
// we can work with the original index [4] instead of [3] to avoid confusion.
configs.splice(1, 1);

return configs;
}

private createJavascriptConfigArray(): Linter.Config[] {
return [{
... eslintJs.configs.all,
files: this.engineConfig.file_extensions.javascript.map(ext => `**/*${ext}`)
}];
}

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

// Add HTML config if HTML files are configured
if (this.engineConfig.file_extensions.html.length > 0) {
const htmlConfig = sldsEslintPlugin.configs['flat/recommended'].find(conf =>
conf.files && conf.files.includes('**/*.html')
);
if (htmlConfig) {
configs.push({
...htmlConfig,
files: this.engineConfig.file_extensions.html.map(ext => `**/*${ext}`)
});
}
}

// Add CSS config if CSS files are configured
if (this.engineConfig.file_extensions.css.length > 0) {
const cssConfig = sldsEslintPlugin.configs['flat/recommended'].find(conf =>
conf.files && conf.files.includes('**/*.{css,scss}')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed in person. If the other team can separate their config so that we didn't need to do these lookups our code would dramatically simplify and be more robust against future changes. So propose to them that they do something like:

Object.assign(plugin.configs, {
  // Flat config for ESLint v9+
  "flat/recommended-css": cssConfigArray,
  "flat/recommended-html": htmlConfigArray,
  "flat/recommended": [... cssConfigArray, ... htmlConfigArray]
...

on their end.

);
if (cssConfig) {
configs.push({
...cssConfig,
files: this.engineConfig.file_extensions.css.map(ext => `**/*${ext}`)
});
}
}

return configs;
}


private createSldsHTMLConfigArray(): Linter.Config[] {
const configs: Linter.Config[] = [];

// Add HTML config if HTML files are configured
const htmlConfig = sldsEslintPlugin.configs['flat/recommended'].find(conf =>
conf.files && conf.files.includes('**/*.html')
);
if (htmlConfig) {
configs.push({
...htmlConfig,
files: this.engineConfig.file_extensions.html.map(ext => `**/*${ext}`)
});
}
return configs;
//Ideally:
// return sldsEslintPlugin.configs['flat/recommended-html'].map((htmlConfig: Linter.Config) => {
// return {
// ...htmlConfig,
// files: this.engineConfig.file_extensions.html.map(ext => `**/*${ext}`)
// };
// });
}

private createSldsCSSConfigArray(): Linter.Config[] {
const configs: Linter.Config[] = [];

// Add CSS config if CSS files are configured
const cssConfig = sldsEslintPlugin.configs['flat/recommended'].find(conf =>
conf.files && conf.files.includes('**/*.{css,scss}')
);
if (cssConfig) {
configs.push({
...cssConfig,
files: this.engineConfig.file_extensions.css.map(ext => `**/*${ext}`)
});
}

return configs;
//Ideally:
// return sldsEslintPlugin.configs['flat/recommended-css'].map((cssConfig: Linter.Config) => {
// return {
// ...cssConfig,
// files: this.engineConfig.file_extensions.css.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 All @@ -126,7 +201,7 @@ export class BaseConfigFactory {
... (conf.languageOptions ?? {}),
parserOptions: {
... (conf.languageOptions?.parserOptions ?? {}),

// Finds the tsconfig.json file nearest to each source file. This should work for most users.
// If not, then we may consider letting user specify this via config or alternatively users can
// just set disable_typescript_base_config=true and configure typescript in their own eslint
Expand All @@ -138,19 +213,23 @@ export class BaseConfigFactory {
}
return configs;
}

private useJsBaseConfig(): boolean {
return !this.engineConfig.disable_javascript_base_config && this.engineConfig.file_extensions.javascript.length > 0;
}

private useLwcBaseConfig(): boolean {
return !this.engineConfig.disable_lwc_base_config && this.engineConfig.file_extensions.javascript.length > 0;
}

private useSldsBaseConfig(): boolean {

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

private useSldsHTMLBaseConfig(): 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 Expand Up @@ -181,7 +260,7 @@ function validateAndGetRawLwcConfigArray(): Linter.Config[] {
// - and lib/configs/recommended.js of https://www.npmjs.com/package/@lwc/eslint-plugin-lwc-platform?activeTab=code
throw new Error("INTERNAL ERROR: The recommended config for @salesforce/eslint-config-lwc or @lwc/eslint-plugin-lwc-platform must have changed.");
}

// Return a shallow copy since we will be making modifications
return rawLwcConfigs.map(config => { return {... config}});
}
Expand Down
Loading
Loading