diff --git a/package-lock.json b/package-lock.json index 121f477e..cd9860fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -802,6 +802,53 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@html-eslint/eslint-plugin": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.34.0.tgz", + "integrity": "sha512-ksMbMQXOsfVHfnopKp9oEaKNiOP0kmFv1DOY54mWxynJ5SkQODYFawKj38CvkJo+89cw5J6+x8tn6ZT6Y2lbPA==", + "license": "MIT", + "dependencies": { + "@html-eslint/template-parser": "^0.34.0", + "@html-eslint/template-syntax-parser": "^0.34.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@html-eslint/parser": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.34.0.tgz", + "integrity": "sha512-gWwcb0nPEq/wKQY+hvGQwdEJ9DXMKs1GLCqG51FqLTTOY1mtwTYRkjmviJCKFwAnQUkYhRrzolYj1ZF2XsTG6A==", + "license": "MIT", + "dependencies": { + "@html-eslint/template-syntax-parser": "^0.34.0", + "es-html-parser": "^1.0.0-alpha.4" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@html-eslint/template-parser": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@html-eslint/template-parser/-/template-parser-0.34.0.tgz", + "integrity": "sha512-hSOEYL5l9vuskklaZj6v5OVYmw6siZYM1g745uexo/YKoHzKSNHkzb/jK5XM6tn6fjYODvCqG04xR8OCn1Dsqw==", + "license": "MIT", + "dependencies": { + "es-html-parser": "^1.0.0-alpha.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@html-eslint/template-syntax-parser": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@html-eslint/template-syntax-parser/-/template-syntax-parser-0.34.0.tgz", + "integrity": "sha512-rmIrw3WtYXITA7AI+LgJqUmjLfWMEP2HNyvvdV8YJysdEOFtM0VXOT9hY8b7mV0ZGqPt/g0dWii1j/94omMufQ==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1638,6 +1685,34 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "license": "MIT" }, + "node_modules/@salesforce-ux/eslint-plugin-slds": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@salesforce-ux/eslint-plugin-slds/-/eslint-plugin-slds-0.5.1.tgz", + "integrity": "sha512-wmaVebSfatYOMrL5w0ESNTvUATaKDwdlQFtMQB1MM3MW/4VSSIokW03sO3w9P+4Sx/evV7JK6K5dB5UEIibAyQ==", + "license": "ISC", + "dependencies": { + "@html-eslint/eslint-plugin": "^0.34.0", + "@html-eslint/parser": "^0.34.0", + "@salesforce-ux/sds-metadata": "^0.3.3" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.0.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + } + } + }, + "node_modules/@salesforce-ux/sds-metadata": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@salesforce-ux/sds-metadata/-/sds-metadata-0.3.3.tgz", + "integrity": "sha512-EAQiHaSBkL54Oy2fJ3Q3muKPXRZA7J68iGA0NJa+7HkqI+96x0w20WIgLTQjR0EO7E3gSJ8/6AHzQHAiKjlKcg==", + "license": "ISC" + }, "node_modules/@salesforce/code-analyzer-core": { "resolved": "packages/code-analyzer-core", "link": true @@ -3523,6 +3598,12 @@ "node": ">= 0.4" } }, + "node_modules/es-html-parser": { + "version": "1.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/es-html-parser/-/es-html-parser-1.0.0-alpha.8.tgz", + "integrity": "sha512-7zQHIugusEuMWjWafkdSwzDWGZ5EbRjSBp5mzfa8kwZoCS1zeiKLNV2SM7vbtPCo8xztWrukg5xZ7OGkYEoEbQ==", + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -9205,12 +9286,13 @@ }, "packages/code-analyzer-eslint-engine": { "name": "@salesforce/code-analyzer-eslint-engine", - "version": "0.29.0", + "version": "0.29.0-SNAPSHOT", "license": "BSD-3-Clause", "dependencies": { "@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/code-analyzer-engine-api": "0.27.0", "@salesforce/code-analyzer-eslint8-engine": "0.5.0", "@salesforce/eslint-config-lwc": "^4.0.0", diff --git a/packages/code-analyzer-eslint-engine/package.json b/packages/code-analyzer-eslint-engine/package.json index 545c7ec9..c3394637 100644 --- a/packages/code-analyzer-eslint-engine/package.json +++ b/packages/code-analyzer-eslint-engine/package.json @@ -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", @@ -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", diff --git a/packages/code-analyzer-eslint-engine/src/base-config.ts b/packages/code-analyzer-eslint-engine/src/base-config.ts index 219133c7..d8c4f286 100644 --- a/packages/code-analyzer-eslint-engine/src/base-config.ts +++ b/packages/code-analyzer-eslint-engine/src/base-config.ts @@ -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"; @@ -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()); } @@ -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[])) { @@ -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; } diff --git a/packages/code-analyzer-eslint-engine/src/config.ts b/packages/code-analyzer-eslint-engine/src/config.ts index 5c773d1d..24a7b4fe 100644 --- a/packages/code-analyzer-eslint-engine/src/config.ts +++ b/packages/code-analyzer-eslint-engine/src/config.ts @@ -28,6 +28,10 @@ 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 @@ -35,9 +39,10 @@ export type ESLintEngineConfig = { // 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. @@ -47,6 +52,7 @@ export type ESLintEngineConfig = { export type FileExtensionsObject = { javascript: string[], typescript: string[], + html: string[], other: string[] }; @@ -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 @@ -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", @@ -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 { @@ -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(), }; diff --git a/packages/code-analyzer-eslint-engine/src/messages.ts b/packages/code-analyzer-eslint-engine/src/messages.ts index b69308e2..e8515196 100644 --- a/packages/code-analyzer-eslint-engine/src/messages.ts +++ b/packages/code-analyzer-eslint-engine/src/messages.ts @@ -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` + @@ -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'.`, diff --git a/packages/code-analyzer-eslint-engine/src/missing-types-for-dependencies.d.ts b/packages/code-analyzer-eslint-engine/src/missing-types-for-dependencies.d.ts index bd22f67b..01e15127 100644 --- a/packages/code-analyzer-eslint-engine/src/missing-types-for-dependencies.d.ts +++ b/packages/code-analyzer-eslint-engine/src/missing-types-for-dependencies.d.ts @@ -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; + readonly configs: { + readonly "flat/recommended": Linter.Config[]; + }; + }; + export = plugin; +} diff --git a/packages/code-analyzer-eslint-engine/src/rule-mappings.ts b/packages/code-analyzer-eslint-engine/src/rule-mappings.ts index a8cbcefb..e59761a4 100644 --- a/packages/code-analyzer-eslint-engine/src/rule-mappings.ts +++ b/packages/code-analyzer-eslint-engine/src/rule-mappings.ts @@ -1595,5 +1595,21 @@ export const RULE_MAPPINGS: Record relevantFileExtensions.includes(path.extname(file).toLowerCase())); @@ -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}`); } diff --git a/packages/code-analyzer-eslint-engine/test/end-to-end.test.ts b/packages/code-analyzer-eslint-engine/test/end-to-end.test.ts index 22e9a6a4..20b6143e 100644 --- a/packages/code-analyzer-eslint-engine/test/end-to-end.test.ts +++ b/packages/code-analyzer-eslint-engine/test/end-to-end.test.ts @@ -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); @@ -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'); diff --git a/packages/code-analyzer-eslint-engine/test/engine.test.ts b/packages/code-analyzer-eslint-engine/test/engine.test.ts index 14a82ac7..295f8e0b 100644 --- a/packages/code-analyzer-eslint-engine/test/engine.test.ts +++ b/packages/code-analyzer-eslint-engine/test/engine.test.ts @@ -59,7 +59,8 @@ describe('Tests for the describeRules method of ESLintEngine', () => { const LWC_CONFIG_RULES: RuleDescription[] = loadRuleDescriptions('rules_OnlyLwcBaseConfig.goldfile.json'); const JS_CONFIG_RULES: RuleDescription[] = loadRuleDescriptions('rules_OnlyJavaScriptBaseConfig.goldfile.json'); const TS_CONFIG_RULES: RuleDescription[] = loadRuleDescriptions('rules_OnlyTypeScriptBaseConfig.goldfile.json'); - const DEFAULT_RULES: RuleDescription[] = makeUniqueAndSorted([...LWC_CONFIG_RULES, ...JS_CONFIG_RULES, ...TS_CONFIG_RULES]); + const SLDS_CONFIG_RULES: RuleDescription[] = loadRuleDescriptions('rules_OnlySldsBaseConfig.goldfile.json'); + const DEFAULT_RULES: RuleDescription[] = makeUniqueAndSorted([...LWC_CONFIG_RULES, ...JS_CONFIG_RULES, ...TS_CONFIG_RULES, ...SLDS_CONFIG_RULES]); const CUSTOM_RULES: RuleDescription[] = loadRuleDescriptions('rules_OnlyCustomConfigWithNewRules.goldfile.json'); // The config for workspaceThatHasCustomConfigModifyingExistingRules modifies some rule properties. This does not @@ -162,7 +163,7 @@ describe('Tests for the describeRules method of ESLintEngine', () => { disable_javascript_base_config: true }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); - expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...LWC_CONFIG_RULES, ...TS_CONFIG_RULES])); + expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...LWC_CONFIG_RULES, ...TS_CONFIG_RULES, ...SLDS_CONFIG_RULES])); }); it('When disable_lwc_base_config=true, then the lwc rules are removed but javascript rules remain', async() => { @@ -170,7 +171,15 @@ describe('Tests for the describeRules method of ESLintEngine', () => { disable_lwc_base_config: true }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); - expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...JS_CONFIG_RULES, ...TS_CONFIG_RULES])); + expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...JS_CONFIG_RULES, ...TS_CONFIG_RULES, ...SLDS_CONFIG_RULES])); + }); + + it('When disable_slds_base_config=true, then the slds rules are removed but other rules remain', async() => { + const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, + disable_slds_base_config: true + }); + const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); + expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...LWC_CONFIG_RULES, ...JS_CONFIG_RULES, ...TS_CONFIG_RULES])); }); it('When disable_typescript_base_config=true, then the typescript rules are removed', async() => { @@ -178,45 +187,60 @@ describe('Tests for the describeRules method of ESLintEngine', () => { disable_typescript_base_config: true }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); - expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...LWC_CONFIG_RULES, ...JS_CONFIG_RULES])); + expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...LWC_CONFIG_RULES, ...JS_CONFIG_RULES, ...SLDS_CONFIG_RULES])); }); - it('When disable_lwc_base_config=true and disable_typescript_base_config=true, then only base javascript rules remain', async() => { + it('When disable_lwc_base_config=true, disable_typescript_base_config=true, and disable_slds_base_config=true, then only base javascript rules remain', async() => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, disable_typescript_base_config: true, disable_lwc_base_config: true, + disable_slds_base_config: true }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); expect(ruleDescriptions).toEqual(JS_CONFIG_RULES); }); - it('When disable_javascript_base_config=true and disable_lwc_base_config=true, then only base typescript rules remain', async() => { + it('When disable_javascript_base_config=true, disable_lwc_base_config=true, and disable_slds_base_config=true, then only base typescript rules remain', async() => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, disable_javascript_base_config: true, disable_lwc_base_config: true, + disable_slds_base_config: true }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); expect(ruleDescriptions).toEqual(TS_CONFIG_RULES); }); - it('When disable_javascript_base_config=true and disable_typescript_base_config=true, then only base lwc rules remain', async() => { + it('When disable_javascript_base_config=true, disable_typescript_base_config=true, and and disable_slds_base_config=true, then only base lwc rules remain', async() => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, disable_javascript_base_config: true, disable_typescript_base_config: true, + disable_slds_base_config: true }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); expect(ruleDescriptions).toEqual(LWC_CONFIG_RULES); }); - it('When all *_javascript_base_config equal true and no custom config exists, then no rules should exist', async() => { + it('When disable_javascript_base_config=true, disable_typescript_base_config=true, and and disable_lwc_base_config=true, then only base slds rules remain', async() => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, disable_javascript_base_config: true, disable_typescript_base_config: true, disable_lwc_base_config: true }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); + expect(ruleDescriptions).toEqual(SLDS_CONFIG_RULES); + }); + + it('When all *_base_config equal true and no custom config exists, then no rules should exist', async() => { + const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, + disable_javascript_base_config: true, + disable_typescript_base_config: true, + disable_lwc_base_config: true, + disable_slds_base_config: true + }); + const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); expect(ruleDescriptions).toHaveLength(0); }); + it('When file_extensions.javascript is empty, then javascript rules do not get picked up', async () => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, file_extensions: { @@ -225,7 +249,7 @@ describe('Tests for the describeRules method of ESLintEngine', () => { } }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); - expect(ruleDescriptions).toEqual(TS_CONFIG_RULES); + expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...TS_CONFIG_RULES, ...SLDS_CONFIG_RULES])); }); it('When file_extensions.typescript is empty, then javascript rules do not get picked up', async () => { @@ -236,15 +260,28 @@ describe('Tests for the describeRules method of ESLintEngine', () => { } }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); - expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...LWC_CONFIG_RULES, ...JS_CONFIG_RULES])); + expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...LWC_CONFIG_RULES, ...JS_CONFIG_RULES, ...SLDS_CONFIG_RULES])); }); - it('When file_extensions.javascript and file_extensions.typescript are both empty, then no rules are returned', async () => { + it('When file_extensions.html is empty, then html rules do not get picked up', async () => { + const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, + file_extensions: { + ... DEFAULT_CONFIG_FOR_TESTING.file_extensions, + html: [] + } + }); + const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); + expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...LWC_CONFIG_RULES, ...JS_CONFIG_RULES, ...TS_CONFIG_RULES])); + }); + + it('When file_extensions are all empty, then no rules are returned', async () => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, file_extensions: { ...DEFAULT_CONFIG_FOR_TESTING.file_extensions, javascript: [], - typescript: [] + typescript: [], + html: [], + other: [] } }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions()); @@ -325,7 +362,8 @@ describe('Tests for the describeRules method of ESLintEngine', () => { auto_discover_eslint_config: true, // Sanity test that we can auto discover in config root disable_javascript_base_config: true, disable_typescript_base_config: true, - disable_lwc_base_config: true + disable_lwc_base_config: true, + disable_slds_base_config: true }); const logEvents: LogEvent[] = []; engine.onEvent(EventType.LogEvent, (event: LogEvent) => logEvents.push(event)); @@ -346,13 +384,13 @@ describe('Tests for the describeRules method of ESLintEngine', () => { expect(ruleDescriptions).toEqual(EXPECTED_RULES_FOR_ONLY_CONFIG_THAT_MODIFIES_EXISTING_RULES); }); - it('When workspace contains a config that globally ignores file, then those files are ignored during rule calculation', async () => { + it('When workspace contains a config that globally ignores js files, then those files are ignored during rule calculation', async () => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, eslint_config_file: path.join(testDataFolder, 'workspaceWithFlatConfigJs', 'a-config-file-that-uses-ignores.js') }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions( new Workspace('id', [path.join(testDataFolder, 'workspaceWithFlatConfigJs')]))); - expect(ruleDescriptions).toEqual(TS_CONFIG_RULES); + expect(ruleDescriptions).toEqual(makeUniqueAndSorted([...TS_CONFIG_RULES, ...SLDS_CONFIG_RULES])); }); it('When a .eslintignore file is auto discovered and a flat eslint config file is specified, then we warn that we ignore it', async () => { @@ -372,11 +410,12 @@ describe('Tests for the describeRules method of ESLintEngine', () => { path.join(testDataFolder, 'workspaceWithLegacyIgnoreFile', '.eslintignore'))); }); - it('When custom rules only apply to file extensions that are not javascript or typescript based, then without specifying file extensions, they are not picked up', async () => { + it('When custom rules only apply to other file extensions, then without specifying file extensions in custom config, they are not picked up', async () => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, disable_lwc_base_config: true, disable_javascript_base_config: true, disable_typescript_base_config: true, + disable_slds_base_config: true, eslint_config_file: path.join(workspaceThatHasCustomConfigWithNewRules, 'eslint-config-only-for-other-files.js') }); const ruleDescriptions: RuleDescription[] = await engine.describeRules(createDescribeOptions( @@ -385,11 +424,12 @@ describe('Tests for the describeRules method of ESLintEngine', () => { expect(ruleDescriptions).toHaveLength(0); }); - it('When custom rules only apply to file extensions that are not javascript or typescript based, then when specifying file extensions, they are picked up', async () => { + it('When custom rules only apply to other file extensions, then when specifying file extensions in custom config, they are picked up', async () => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, disable_lwc_base_config: true, disable_javascript_base_config: true, disable_typescript_base_config: true, + disable_slds_base_config: true, eslint_config_file: path.join(workspaceThatHasCustomConfigWithNewRules, 'eslint-config-only-for-other-files.js'), file_extensions:{ ... DEFAULT_CONFIG.file_extensions, @@ -459,16 +499,31 @@ describe('Typical tests for the runRules method of ESLintEngine', () => { "primaryLocationIndex": 0, "ruleName": "@typescript-eslint/no-wrapper-object-types" }; + const expectedHTMLViolation_enforceBemUsage: Violation = { + "codeLocations": [ + { + "endColumn": 27, + "endLine": 1, + "file": path.join(workspaceWithNoCustomConfig, 'dummy1.html'), + "startColumn": 11, + "startLine": 1 + } + ], + "message": "slds-m-top--none has been retired. Update it to the new name slds-m-top_none.", + "primaryLocationIndex": 0, + "ruleName": "@salesforce-ux/slds/enforce-bem-usage" + }; - it('When running with defaults and no customizations, then violations for javascript and typescript are found correctly', async () => { + it('When running with defaults and no customizations, then violations for javascript, typescript and html are found correctly', async () => { const engine: Engine = await createEngineFromPlugin(DEFAULT_CONFIG_FOR_TESTING); const runOptions: RunOptions = createRunOptions(new Workspace('id', [workspaceWithNoCustomConfig])); - const results: EngineRunResults = await engine.runRules(['no-invalid-regexp', '@typescript-eslint/no-wrapper-object-types'], runOptions); + const results: EngineRunResults = await engine.runRules(['no-invalid-regexp', '@typescript-eslint/no-wrapper-object-types', '@salesforce-ux/slds/enforce-bem-usage'], runOptions); - expect(results.violations).toHaveLength(3); + expect(results.violations).toHaveLength(4); expect(results.violations).toContainEqual(expectedJsViolation_noInvalidRegexp); expect(results.violations).toContainEqual(expectedTsViolation_noInvalidRegexp); expect(results.violations).toContainEqual(expectedTsViolation_noWrapperObjectTypes); + expect(results.violations).toContainEqual(expectedHTMLViolation_enforceBemUsage); }); it('When workspace only targets javascript files, then only javascript violations are returned', async () => { @@ -488,7 +543,15 @@ describe('Typical tests for the runRules method of ESLintEngine', () => { expect(results.violations).toEqual([expectedTsViolation_noInvalidRegexp]); }); - it('When workspace does not contains javascript or typescript files, then zero violations are returned', async () => { + it('When workspace only contains html files, then only html violations are returned', async () => { + const engine: Engine = await createEngineFromPlugin(DEFAULT_CONFIG_FOR_TESTING); + const runOptions: RunOptions = createRunOptions(new Workspace('id', [path.join(workspaceWithNoCustomConfig, 'dummy1.html')])); + const results: EngineRunResults = await engine.runRules(['@salesforce-ux/slds/enforce-bem-usage'], runOptions); + + expect(results.violations).toEqual([expectedHTMLViolation_enforceBemUsage]); + }); + + it('When workspace does not contains javascript, typescript or html files, then zero violations are returned', async () => { const engine: Engine = await createEngineFromPlugin(DEFAULT_CONFIG_FOR_TESTING); const runOptions: RunOptions = createRunOptions(new Workspace('id', [path.join(workspaceWithNoCustomConfig, 'dummy3.txt')])); const results: EngineRunResults = await engine.runRules(['no-invalid-regexp'], runOptions); @@ -547,7 +610,7 @@ describe('Typical tests for the runRules method of ESLintEngine', () => { }); - it('When custom rules only apply to file extensions that are not javascript or typescript based, then when specifying file extensions, the rules run', async () => { + it('When custom rules only apply to file extensions that are not javascript, typescript, or html based, then when specifying file extensions, the rules run', async () => { const engine: Engine = await createEngineFromPlugin({...DEFAULT_CONFIG_FOR_TESTING, eslint_config_file: path.join(workspaceThatHasCustomConfigWithNewRules, 'eslint-config-only-for-other-files.js'), file_extensions:{ @@ -582,7 +645,7 @@ describe('Typical tests for the runRules method of ESLintEngine', () => { engine.onEvent(EventType.LogEvent, (event: LogEvent) => logEvents.push(event)); const runOptions: RunOptions = createRunOptions(new Workspace('id', [path.join(testDataFolder,'workspaceWithFlatConfigJs')])); - const results: EngineRunResults = await engine.runRules(['no-invalid-regexp', '@typescript-eslint/no-wrapper-object-types'], runOptions); + const results: EngineRunResults = await engine.runRules(['no-invalid-regexp', '@typescript-eslint/no-wrapper-object-types', '@salesforce-ux/slds/enforce-bem-usage'], runOptions); expect(results.violations).toHaveLength(2); // Should not contain js violations but should contain ts violations expect(path.extname(results.violations[0].codeLocations[0].file)).toEqual('.ts'); @@ -734,14 +797,14 @@ describe('Tests for emitting events', () => { await engine.describeRules(createDescribeOptions()); // TODO: We should make our DescribeRulesProgressEvents more refined while calculating the eslint context information expect(describeRulesProgressEvents.map(e => e.percentComplete)).toEqual( - [0, 10, 14, 18, 33, 48, 63, 78, 82, 86, 90, 95, 100]); + [0, 10, 14, 18, 26.57, 35.14, 43.71, 52.29, 60.86, 69.43, 78, 82, 86, 90, 95, 100]); }); it('When runRules is called, then it emits correct progress events', async () => { const runOptions: RunOptions = createRunOptions(new Workspace('id', [workspaceWithNoCustomConfig])); await engine.runRules(['no-unused-vars'], runOptions); expect(runRulesProgressEvents.map(e => e.percentComplete)).toEqual( - [0, 1.5, 3, 14.25, 25.5, 27, 28.5, 30, 62.5, 95, 100]); + [0, 1.5, 3, 10.5, 18, 25.5, 27, 28.5, 30, 51.67, 73.33, 95, 100]); }); }); diff --git a/packages/code-analyzer-eslint-engine/test/misc.test.ts b/packages/code-analyzer-eslint-engine/test/misc.test.ts index 019cc504..f0a73e02 100644 --- a/packages/code-analyzer-eslint-engine/test/misc.test.ts +++ b/packages/code-analyzer-eslint-engine/test/misc.test.ts @@ -34,6 +34,7 @@ describe("Miscellaneous tests that test sensitive implementation details more di const fileExts: FileExtensionsObject = { javascript: ['.js'], typescript: ['.ts'], + html: ['.html'], other: [] } diff --git a/packages/code-analyzer-eslint-engine/test/plugin.test.ts b/packages/code-analyzer-eslint-engine/test/plugin.test.ts index c1fd20a7..1b0c3d53 100644 --- a/packages/code-analyzer-eslint-engine/test/plugin.test.ts +++ b/packages/code-analyzer-eslint-engine/test/plugin.test.ts @@ -49,7 +49,8 @@ describe('Tests for the ESLintEnginePlugin', () => { await expect(callCreateEngineConfig(plugin, userProvidedOverrides)).rejects.toThrow( getMessageFromCatalog(SHARED_MESSAGE_CATALOG, 'ConfigObjectContainsInvalidKey', 'engines.eslint', 'dummy', '["auto_discover_eslint_config","disable_javascript_base_config","disable_lwc_base_config",' + - '"disable_typescript_base_config","eslint_config_file","eslint_ignore_file","file_extensions"]')); + '"disable_slds_base_config","disable_typescript_base_config","eslint_config_file","eslint_ignore_file",' + + '"file_extensions"]')); }); it('When a valid legacy eslint_config_file is passed to createEngineConfig, then it is set on the config', async () => { @@ -169,6 +170,14 @@ describe('Tests for the ESLintEnginePlugin', () => { 'engines.eslint.disable_lwc_base_config', 'boolean', 'object')); }); + it('When disable_slds_base_config is passed to createEngineConfig, then it is set on the config', async () => { + const userProvidedOverrides: ConfigObject = { + disable_slds_base_config: true + }; + const resolvedConfig: ConfigObject = await callCreateEngineConfig(plugin, userProvidedOverrides); + expect(resolvedConfig['disable_slds_base_config']).toEqual(true); + }); + it('When disable_typescript_base_config is passed to createEngineConfig, then it is set on the config', async () => { const userProvidedOverrides1: ConfigObject = { disable_typescript_base_config: true @@ -194,7 +203,7 @@ describe('Tests for the ESLintEnginePlugin', () => { }; await expect(callCreateEngineConfig(plugin, userProvidedOverrides)).rejects.toThrow( getMessageFromCatalog(SHARED_MESSAGE_CATALOG, 'ConfigObjectContainsInvalidKey', 'engines.eslint.file_extensions', - 'oops', '["javascript","other","typescript"]')); + 'oops', '["html","javascript","other","typescript"]')); }); it('When a valid file_extensions.javascript value is passed to createEngineConfig, then it is set on the config', async () => { @@ -220,6 +229,30 @@ describe('Tests for the ESLintEnginePlugin', () => { 'engines.eslint.file_extensions.javascript[0]', 'string', 'number')); }); + it('When a valid file_extensions.html value is passed to createEngineConfig, then it is set on the config', async () => { + const userProvidedOverrides: ConfigObject = { + file_extensions: { + html: ['.html'] + } + }; + const resolvedConfig: ConfigObject = await callCreateEngineConfig(plugin, userProvidedOverrides); + expect(resolvedConfig['file_extensions']).toEqual({ + ...DEFAULT_CONFIG.file_extensions, + html: ['.html'] + }); + }); + + it('When file_extensions.html is invalid, then createEngineConfig errors', async () => { + const userProvidedOverrides: ConfigObject = { + file_extensions: { + html: [false] + } + }; + await expect(callCreateEngineConfig(plugin, userProvidedOverrides)).rejects.toThrow( + getMessageFromCatalog(SHARED_MESSAGE_CATALOG, 'ConfigValueMustBeOfType', + 'engines.eslint.file_extensions.html[0]', 'string', 'boolean')); + }); + it('When a valid file_extensions.typescript value is passed to createEngineConfig, then it is set on the config', async () => { const userProvidedOverrides: ConfigObject = { file_extensions: { @@ -255,6 +288,17 @@ describe('Tests for the ESLintEnginePlugin', () => { 'engines.eslint.file_extensions', '.js', '["javascript","typescript"]')); }); + it('When a supported extension is listed under other language in file_extensions, then createEngineConfig errors', async () => { + const userProvidedOverrides: ConfigObject = { + file_extensions: { + other: ['.html'] + } + }; + await expect(callCreateEngineConfig(plugin, userProvidedOverrides)).rejects.toThrow( + getMessage('InvalidFileExtensionDueToItBeingListedTwice', + 'engines.eslint.file_extensions', '.html', '["html","other"]')); + }); + it('When createEngine is passed an invalid engine name, then an error is thrown', async () => { await expect(plugin.createEngine('oops', DEFAULT_CONFIG)).rejects.toThrow( getMessage('UnsupportedEngineName' ,'oops')); diff --git a/packages/code-analyzer-eslint-engine/test/test-data/rules_OnlySldsBaseConfig.goldfile.json b/packages/code-analyzer-eslint-engine/test/test-data/rules_OnlySldsBaseConfig.goldfile.json new file mode 100644 index 00000000..559a2c3f --- /dev/null +++ b/packages/code-analyzer-eslint-engine/test/test-data/rules_OnlySldsBaseConfig.goldfile.json @@ -0,0 +1,41 @@ +[ + { + "description": "Replace BEM double-dash syntax in class names with single underscore syntax.", + "name": "@salesforce-ux/slds/enforce-bem-usage", + "resourceUrls": [ + "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#enforce-bem-usage" + ], + "severityLevel": 4, + "tags": [ + "Recommended", + "BestPractices", + "HTML" + ] + }, + { + "description": "Update component attributes or CSS classes for the modal close button to comply with the modal component blueprint.", + "name": "@salesforce-ux/slds/modal-close-button-issue", + "resourceUrls": [ + "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#modal-close-button-issue" + ], + "severityLevel": 3, + "tags": [ + "Recommended", + "BestPractices", + "HTML" + ] + }, + { + "description": "Replace classes that aren’t available with SLDS 2 classes. See lightningdesignsystem.com for more info.", + "name": "@salesforce-ux/slds/no-deprecated-classes-slds2", + "resourceUrls": [ + "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-deprecated-classes-slds2" + ], + "severityLevel": 2, + "tags": [ + "Recommended", + "ErrorProne", + "HTML" + ] + } +] \ No newline at end of file diff --git a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigCjs/dummy1.html b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigCjs/dummy1.html new file mode 100644 index 00000000..f0502ff8 --- /dev/null +++ b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigCjs/dummy1.html @@ -0,0 +1 @@ +

Oh bother

\ No newline at end of file diff --git a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigCjs/eslint.config.cjs b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigCjs/eslint.config.cjs index 220d6b0e..d2afb1e3 100644 --- a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigCjs/eslint.config.cjs +++ b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigCjs/eslint.config.cjs @@ -1,6 +1,4 @@ -const { defineConfig } = require("eslint/config"); - -module.exports = defineConfig([ +module.exports = [ { rules: { "array-callback-return": ["error"], @@ -9,4 +7,4 @@ module.exports = defineConfig([ "no-useless-escape": [0] }, }, -]); +]; diff --git a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigJs/a-config-file-that-uses-ignores.js b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigJs/a-config-file-that-uses-ignores.js index 4547672d..3d9334ac 100644 --- a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigJs/a-config-file-that-uses-ignores.js +++ b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigJs/a-config-file-that-uses-ignores.js @@ -1,23 +1,7 @@ -const { defineConfig } = require("eslint/config"); - -module.exports = defineConfig([ +module.exports = [ { // Should globally ignore js files - making the base js and lwc config not applicable // See https://eslint.org/docs/latest/use/configure/configuration-files#globally-ignoring-files-with-ignores ignores: ["**/*.js"] - }, - { - rules: { - "@typescript-eslint/no-unused-vars": ["error", - { - "args": "all", - "argsIgnorePattern": "^_", - "caughtErrors": "all", - "caughtErrorsIgnorePattern": "^_", - "destructuredArrayIgnorePattern": "^_", - "varsIgnorePattern": "^_", - "ignoreRestSiblings": true - }] - } } -]); +]; diff --git a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigJs/dummy1.html b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigJs/dummy1.html new file mode 100644 index 00000000..b06b0684 --- /dev/null +++ b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigJs/dummy1.html @@ -0,0 +1 @@ +

Oh hi!

\ No newline at end of file diff --git a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigMjs/dummy1.html b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigMjs/dummy1.html new file mode 100644 index 00000000..13321850 --- /dev/null +++ b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigMjs/dummy1.html @@ -0,0 +1 @@ +

Hola!

\ No newline at end of file diff --git a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigMjs/eslint.config.mjs b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigMjs/eslint.config.mjs index 5e56b827..bde6b602 100644 --- a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigMjs/eslint.config.mjs +++ b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigMjs/eslint.config.mjs @@ -1,6 +1,4 @@ -import { defineConfig } from "eslint/config"; - -export default defineConfig([ +export default [ { rules: { "array-callback-return": ["error"], @@ -9,4 +7,4 @@ export default defineConfig([ "no-useless-escape": [0] }, }, -]); +]; diff --git a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigWithNewRules/dummy1.html b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigWithNewRules/dummy1.html new file mode 100644 index 00000000..3af20303 --- /dev/null +++ b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithFlatConfigWithNewRules/dummy1.html @@ -0,0 +1 @@ +

Oh hello!

\ No newline at end of file diff --git a/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithLegacyIgnoreFile/dummy1.html b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithLegacyIgnoreFile/dummy1.html new file mode 100644 index 00000000..66081481 --- /dev/null +++ b/packages/code-analyzer-eslint-engine/test/test-data/workspaceWithLegacyIgnoreFile/dummy1.html @@ -0,0 +1 @@ +

Oh no

\ No newline at end of file diff --git a/packages/code-analyzer-eslint-engine/test/test-data/workspace_NoCustomConfig/dummy1.html b/packages/code-analyzer-eslint-engine/test/test-data/workspace_NoCustomConfig/dummy1.html new file mode 100644 index 00000000..aa2b4e1f --- /dev/null +++ b/packages/code-analyzer-eslint-engine/test/test-data/workspace_NoCustomConfig/dummy1.html @@ -0,0 +1 @@ +

Bonjour!

\ No newline at end of file