diff --git a/package-lock.json b/package-lock.json index aadf47bb..0ca0c389 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2473,9 +2473,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001685", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001685.tgz", - "integrity": "sha512-e/kJN1EMyHQzgcMEEgoo+YTCO1NGCmIYHk5Qk8jT6AazWemS5QFKJ5ShCJlH3GZrNIdZofcNCEwZqbMjjKzmnA==", + "version": "1.0.30001686", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001686.tgz", + "integrity": "sha512-Y7deg0Aergpa24M3qLC5xjNklnKnhsmSyR/V89dLZ1n0ucJIFNs7PgR2Yfa/Zf6W79SbBicgtGxZr2juHkEUIA==", "funding": [ { "type": "opencollective", diff --git a/packages/code-analyzer-core/src/code-analyzer.ts b/packages/code-analyzer-core/src/code-analyzer.ts index 6fd33f5a..06712b10 100644 --- a/packages/code-analyzer-core/src/code-analyzer.ts +++ b/packages/code-analyzer-core/src/code-analyzer.ts @@ -238,15 +238,16 @@ export class CodeAnalyzer { return; } + const engineOverrides: EngineOverrides = this.config.getEngineOverridesFor(engineName); + try { const engineConfigDescription: engApi.ConfigDescription = enginePluginV1.describeEngineConfig(engineName); - normalizeEngineConfigDescription(engineConfigDescription, engineName); - this.engineConfigDescriptions.set(engineName, engineConfigDescription); + const configDescription: ConfigDescription = toConfigDescription(engineConfigDescription, engineName, engineOverrides); + this.engineConfigDescriptions.set(engineName, configDescription); } catch (err) { throw new Error(getMessage('PluginErrorWhenCreatingEngine', engineName, (err as Error).message)); } - const engineOverrides: EngineOverrides = this.config.getEngineOverridesFor(engineName); if (engineOverrides[FIELDS.DISABLE_ENGINE]) { this.emitLogEvent(LogLevel.Debug, getMessage('EngineDisabled', engineName, `${FIELDS.ENGINES}.${engineName}.${FIELDS.DISABLE_ENGINE}`)) @@ -524,15 +525,31 @@ function isIntegerBetween(value: number, leftBound: number, rightBound: number): return value >= leftBound && value <= rightBound && Number.isInteger(value); } -function normalizeEngineConfigDescription(engineConfigDescription: ConfigDescription, engineName: string): void { - // Every engine config should have an overview, so if missing, then we add in a generic one - if (!engineConfigDescription.overview) { - engineConfigDescription.overview = getMessage('GenericEngineConfigOverview', engineName.toUpperCase()); +/** + * Converts an engApi.ConfigDescription into a normalized ConfigDescription + */ +function toConfigDescription(engineConfigDescription: engApi.ConfigDescription, engineName: string, + engineOverrides: EngineOverrides): ConfigDescription { + const configDescription: ConfigDescription = { + // Every engine config should have an overview, so if missing, then we add in a generic one + overview: engineConfigDescription.overview ? engineConfigDescription.overview : + getMessage('GenericEngineConfigOverview', engineName.toUpperCase()), + + fieldDescriptions: { + // Every engine config should have a disable_engine field which we prefer to be first in the object for display purposes + [FIELDS.DISABLE_ENGINE]: { + descriptionText: getMessage('EngineConfigFieldDescription_disable_engine', engineName), + valueType: "boolean", + defaultValue: false, + wasSuppliedByUser: FIELDS.DISABLE_ENGINE in engineOverrides + } + } } - - // Every engine config should have a disable_engine field which we prefer to be first in the object for display purposes - engineConfigDescription.fieldDescriptions = { - [FIELDS.DISABLE_ENGINE]: getMessage('EngineConfigFieldDescription_disable_engine', engineName), - ... engineConfigDescription.fieldDescriptions + for (const fieldName in engineConfigDescription.fieldDescriptions) { + configDescription.fieldDescriptions[fieldName] = { + ... engineConfigDescription.fieldDescriptions[fieldName], + wasSuppliedByUser: fieldName in engineOverrides + }; } + return configDescription; } \ No newline at end of file diff --git a/packages/code-analyzer-core/src/config.ts b/packages/code-analyzer-core/src/config.ts index 33d8eb37..f845f786 100644 --- a/packages/code-analyzer-core/src/config.ts +++ b/packages/code-analyzer-core/src/config.ts @@ -6,7 +6,6 @@ import * as yaml from 'js-yaml'; import {getMessage} from "./messages"; import {toAbsolutePath} from "./utils" import {SeverityLevel} from "./rules"; -import {getMessageFromCatalog, SHARED_MESSAGE_CATALOG, ValueValidator} from "@salesforce/code-analyzer-engine-api"; export const FIELDS = { CONFIG_ROOT: 'config_root', @@ -27,15 +26,6 @@ export type RuleOverride = { severity?: SeverityLevel tags?: string[] } -export const TOP_LEVEL_CONFIG_DESCRIPTION: ConfigDescription = { - overview: getMessage('ConfigOverview'), - fieldDescriptions: { - config_root: getMessage('ConfigFieldDescription_config_root'), - log_folder: getMessage('ConfigFieldDescription_log_folder'), - rules: getMessage('ConfigFieldDescription_rules'), - engines: getMessage('ConfigFieldDescription_engines'), - } -} type TopLevelConfig = { config_root: string @@ -53,7 +43,20 @@ export const DEFAULT_CONFIG: TopLevelConfig = { custom_engine_plugin_modules: [], // INTERNAL USE ONLY }; -export type ConfigDescription = engApi.ConfigDescription; +export type ConfigDescription = { + // A brief overview of this specific configuration object. It is recommended to include a link to documentation when possible. + overview: string + + // Description objects for the primary fields in the configuration + fieldDescriptions: Record +} + +export type ConfigFieldDescription = engApi.ConfigFieldDescription & { + // Whether or not the user has supplied a value for the field in their configuration file + // Note: Unlike the Engine API's ConfigFieldDescription, core has the ability to determine if the user supplied + // the field value (as we create a CodeAnalyzerConfig object), which is why "wasSuppliedByUser" is here only. + wasSuppliedByUser: boolean +} export class CodeAnalyzerConfig { private readonly config: TopLevelConfig; @@ -99,7 +102,7 @@ export class CodeAnalyzerConfig { config_root: configRoot, log_folder: configExtractor.extractFolder(FIELDS.LOG_FOLDER, DEFAULT_CONFIG.log_folder)!, custom_engine_plugin_modules: configExtractor.extractArray(FIELDS.CUSTOM_ENGINE_PLUGIN_MODULES, - ValueValidator.validateString, + engApi.ValueValidator.validateString, DEFAULT_CONFIG.custom_engine_plugin_modules)!, rules: extractRulesValue(configExtractor), engines: extractEnginesValue(configExtractor) @@ -107,8 +110,36 @@ export class CodeAnalyzerConfig { return new CodeAnalyzerConfig(config); } - public static getConfigDescription(): ConfigDescription { - return TOP_LEVEL_CONFIG_DESCRIPTION; + public getConfigDescription(): ConfigDescription { + return { + overview: getMessage('ConfigOverview'), + fieldDescriptions: { + config_root: { + descriptionText: getMessage('ConfigFieldDescription_config_root'), + valueType: 'string', + defaultValue: null, // Using null for doc and since it indicates that the value is calculated based on the environment + wasSuppliedByUser: this.config.config_root !== DEFAULT_CONFIG.config_root + }, + log_folder: { + descriptionText: getMessage('ConfigFieldDescription_log_folder'), + valueType: 'string', + defaultValue: null, // Using null for doc and since it indicates that the value is calculated based on the environment + wasSuppliedByUser: this.config.log_folder !== DEFAULT_CONFIG.log_folder + }, + rules: { + descriptionText: getMessage('ConfigFieldDescription_rules'), + valueType: 'object', + defaultValue: {}, + wasSuppliedByUser: this.config.rules !== DEFAULT_CONFIG.rules + }, + engines: { + descriptionText: getMessage('ConfigFieldDescription_engines'), + valueType: 'object', + defaultValue: {}, + wasSuppliedByUser: this.config.engines !== DEFAULT_CONFIG.engines + } + } + }; } private constructor(config: TopLevelConfig) { @@ -160,7 +191,7 @@ function extractRuleOverridesFrom(engineRuleOverridesExtractor: engApi.ConfigVal function extractRuleOverrideFrom(ruleOverrideExtractor: engApi.ConfigValueExtractor): RuleOverride { const engSeverity: engApi.SeverityLevel | undefined = ruleOverrideExtractor.extractSeverityLevel(FIELDS.SEVERITY); return { - tags: ruleOverrideExtractor.extractArray(FIELDS.TAGS, ValueValidator.validateString), + tags: ruleOverrideExtractor.extractArray(FIELDS.TAGS, engApi.ValueValidator.validateString), severity: engSeverity === undefined ? undefined : engSeverity as SeverityLevel } } @@ -194,17 +225,19 @@ function parseAndValidate(parseFcn: () => unknown): object { function validateAbsoluteFolder(value: unknown, fieldPath: string): string { const folderValue: string = validateAbsolutePath(value, fieldPath); if (!fs.statSync(folderValue).isDirectory()) { - throw new Error(getMessageFromCatalog(SHARED_MESSAGE_CATALOG, 'ConfigFolderValueMustNotBeFile', fieldPath, folderValue)); + throw new Error(engApi.getMessageFromCatalog(engApi.SHARED_MESSAGE_CATALOG, + 'ConfigFolderValueMustNotBeFile', fieldPath, folderValue)); } return folderValue; } function validateAbsolutePath(value: unknown, fieldPath: string): string { - const pathValue: string = ValueValidator.validateString(value, fieldPath); + const pathValue: string = engApi.ValueValidator.validateString(value, fieldPath); if (pathValue !== toAbsolutePath(pathValue)) { throw new Error(getMessage('ConfigPathValueMustBeAbsolute', fieldPath, pathValue, toAbsolutePath(pathValue))); } else if (!fs.existsSync(pathValue)) { - throw new Error(getMessageFromCatalog(SHARED_MESSAGE_CATALOG, 'ConfigPathValueDoesNotExist', fieldPath, pathValue)); + throw new Error(engApi.getMessageFromCatalog(engApi.SHARED_MESSAGE_CATALOG, + 'ConfigPathValueDoesNotExist', fieldPath, pathValue)); } return pathValue; } \ No newline at end of file diff --git a/packages/code-analyzer-core/src/index.ts b/packages/code-analyzer-core/src/index.ts index 10e0c692..f8d58bf4 100644 --- a/packages/code-analyzer-core/src/index.ts +++ b/packages/code-analyzer-core/src/index.ts @@ -1,6 +1,7 @@ export { CodeAnalyzerConfig, ConfigDescription, + ConfigFieldDescription, EngineOverrides, RuleOverrides, RuleOverride diff --git a/packages/code-analyzer-core/test/add-engines.test.ts b/packages/code-analyzer-core/test/add-engines.test.ts index 089d031b..15ed0c45 100644 --- a/packages/code-analyzer-core/test/add-engines.test.ts +++ b/packages/code-analyzer-core/test/add-engines.test.ts @@ -185,16 +185,31 @@ describe("Tests for adding engines to Code Analyzer", () => { expect(engineConfigDescription1).toEqual({ overview: "OverviewForStub1", fieldDescriptions: { - disable_engine: getMessage('EngineConfigFieldDescription_disable_engine', 'stubEngine1'), - miscSetting1: "someDescriptionFor_miscSetting1" + disable_engine: { + descriptionText: getMessage('EngineConfigFieldDescription_disable_engine', 'stubEngine1'), + valueType: "boolean", + defaultValue: false, + wasSuppliedByUser: false + }, + misc_value: { + descriptionText: "someDescriptionFor_misc_value", + valueType: "number", + defaultValue: 1987, + wasSuppliedByUser: false + } } }); const engineConfigDescription2: ConfigDescription = codeAnalyzer.getEngineConfigDescription('stubEngine2'); expect(engineConfigDescription2).toEqual({ overview: getMessage('GenericEngineConfigOverview', 'STUBENGINE2'), fieldDescriptions: { - disable_engine: getMessage('EngineConfigFieldDescription_disable_engine', 'stubEngine2') - } + disable_engine: { + descriptionText: getMessage('EngineConfigFieldDescription_disable_engine', 'stubEngine2'), + valueType: "boolean", + defaultValue: false, + wasSuppliedByUser: false + } + }, }); }); @@ -220,8 +235,18 @@ describe("Tests for adding engines to Code Analyzer", () => { expect(codeAnalyzer.getEngineConfigDescription('stubEngine1')).toEqual({ overview: "OverviewForStub1", fieldDescriptions: { - disable_engine: getMessage('EngineConfigFieldDescription_disable_engine', 'stubEngine1'), - miscSetting1: "someDescriptionFor_miscSetting1" + disable_engine: { + descriptionText: getMessage('EngineConfigFieldDescription_disable_engine', 'stubEngine1'), + valueType: "boolean", + defaultValue: false, + wasSuppliedByUser: true + }, + misc_value: { + descriptionText: "someDescriptionFor_misc_value", + valueType: "number", + defaultValue: 1987, + wasSuppliedByUser: true + } } }); }); diff --git a/packages/code-analyzer-core/test/config.test.ts b/packages/code-analyzer-core/test/config.test.ts index 9d771987..e2b40dbf 100644 --- a/packages/code-analyzer-core/test/config.test.ts +++ b/packages/code-analyzer-core/test/config.test.ts @@ -1,10 +1,10 @@ import {CodeAnalyzerConfig, SeverityLevel} from "../src"; import * as os from "node:os"; import * as path from "node:path"; -import {ConfigDescription, getMessageFromCatalog, SHARED_MESSAGE_CATALOG} from "@salesforce/code-analyzer-engine-api"; +import {getMessageFromCatalog, SHARED_MESSAGE_CATALOG} from "@salesforce/code-analyzer-engine-api"; import {getMessage} from "../src/messages"; import {changeWorkingDirectoryToPackageRoot} from "./test-helpers"; -import {TOP_LEVEL_CONFIG_DESCRIPTION, DEFAULT_CONFIG} from "../src/config"; +import {ConfigDescription, DEFAULT_CONFIG} from "../src/config"; changeWorkingDirectoryToPackageRoot(); @@ -273,8 +273,45 @@ describe("Tests for creating and accessing configuration values", () => { 'engines.stubEngine1.disable_engine', 'boolean', 'number')); }); - it("When getConfigDescription is called, then it returns our expected description object", () => { - const configDescription: ConfigDescription = CodeAnalyzerConfig.getConfigDescription(); - expect(configDescription).toEqual(TOP_LEVEL_CONFIG_DESCRIPTION); + it("When getConfigDescription is called from default config, then it returns our expected description object", () => { + const configDescription: ConfigDescription = CodeAnalyzerConfig.withDefaults().getConfigDescription(); + expect(configDescription.overview).toEqual(getMessage('ConfigOverview')); + expect(configDescription.fieldDescriptions).toEqual({ + config_root: { + descriptionText: getMessage('ConfigFieldDescription_config_root'), + valueType: 'string', + defaultValue: null, + wasSuppliedByUser: false + }, + log_folder: { + descriptionText: getMessage('ConfigFieldDescription_log_folder'), + valueType: 'string', + defaultValue: null, + wasSuppliedByUser: false + }, + rules: { + descriptionText: getMessage('ConfigFieldDescription_rules'), + valueType: 'object', + defaultValue: {}, + wasSuppliedByUser: false + }, + engines: { + descriptionText: getMessage('ConfigFieldDescription_engines'), + valueType: 'object', + defaultValue: {}, + wasSuppliedByUser: false + } + }); + }); + + it("When getConfigDescription is called from modified config, then it correctly sets wasSuppliedByUser fields", () => { + const configDescription: ConfigDescription = CodeAnalyzerConfig.fromObject( + {config_root: __dirname, rules: {"someEngine": {"abc": {severity: SeverityLevel.High}}}} + ).getConfigDescription(); + expect(configDescription.overview).toEqual(getMessage('ConfigOverview')); + expect(configDescription.fieldDescriptions.config_root.wasSuppliedByUser).toEqual(true); + expect(configDescription.fieldDescriptions.log_folder.wasSuppliedByUser).toEqual(false); + expect(configDescription.fieldDescriptions.rules.wasSuppliedByUser).toEqual(true); + expect(configDescription.fieldDescriptions.engines.wasSuppliedByUser).toEqual(false); }); }); \ No newline at end of file diff --git a/packages/code-analyzer-core/test/stubs.ts b/packages/code-analyzer-core/test/stubs.ts index 61c2fca4..6abcf22a 100644 --- a/packages/code-analyzer-core/test/stubs.ts +++ b/packages/code-analyzer-core/test/stubs.ts @@ -26,7 +26,11 @@ export class StubEnginePlugin extends engApi.EnginePluginV1 { return { overview: 'OverviewForStub1', fieldDescriptions: { - miscSetting1: "someDescriptionFor_miscSetting1" + misc_value: { + descriptionText: "someDescriptionFor_misc_value", + valueType: "number", + defaultValue: 1987 + } } } } diff --git a/packages/code-analyzer-engine-api/src/config.ts b/packages/code-analyzer-engine-api/src/config.ts index d7ec8832..1552ad81 100644 --- a/packages/code-analyzer-engine-api/src/config.ts +++ b/packages/code-analyzer-engine-api/src/config.ts @@ -18,8 +18,26 @@ export type ConfigDescription = { // A brief overview of the configuration. It is recommended to include a link to documentation when possible. overview?: string - // Description messages of the top-level fields in the configuration - fieldDescriptions?: Record + // Description objects for the primary fields in the configuration + fieldDescriptions?: Record +} + +export type ConfigFieldDescription = { + // Text that describes the field to be documented + descriptionText: string + + // The type for the value associated with this field (as a string). + // Note that this as a string instead of an enum in order to give flexibility to describe the type of the field as + // we want it to show up in our public docs, but it is recommended to return one of the following when possible: + // 'string', 'number', 'boolean', 'object', 'array' + valueType: string + + // The default value that is to be documented. Use null if you do not have a fixed default value. + // Note that this value may not necessarily be the same that could be auto calculated if the user doesn't provide + // the value in their configuration file. For example, we may want to report null as the default value for a + // java_command field whenever the user doesn't explicitly provide one even though it might automatically + // get calculated at runtime to be some java command found in their environment. + defaultValue: ConfigValue } export class ConfigValueExtractor { diff --git a/packages/code-analyzer-engine-api/src/index.ts b/packages/code-analyzer-engine-api/src/index.ts index 9af185a2..afdf7dcd 100644 --- a/packages/code-analyzer-engine-api/src/index.ts +++ b/packages/code-analyzer-engine-api/src/index.ts @@ -1,5 +1,6 @@ export { ConfigDescription, + ConfigFieldDescription, ConfigObject, ConfigValue, ConfigValueExtractor, diff --git a/packages/code-analyzer-eslint-engine/src/config.ts b/packages/code-analyzer-eslint-engine/src/config.ts index 104e5dd3..de4c4642 100644 --- a/packages/code-analyzer-eslint-engine/src/config.ts +++ b/packages/code-analyzer-eslint-engine/src/config.ts @@ -3,20 +3,6 @@ import {getMessage} from "./messages"; import path from "node:path"; import {makeUnique} from "./utils"; -export const ESLINT_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { - overview: getMessage('ConfigOverview'), - fieldDescriptions: { - eslint_config_file: getMessage('ConfigFieldDescription_eslint_config_file'), - eslint_ignore_file: getMessage('ConfigFieldDescription_eslint_ignore_file'), - auto_discover_eslint_config: getMessage('ConfigFieldDescription_auto_discover_eslint_config'), - disable_javascript_base_config: getMessage('ConfigFieldDescription_disable_javascript_base_config'), - disable_lwc_base_config: getMessage('ConfigFieldDescription_disable_lwc_base_config'), - disable_typescript_base_config: getMessage('ConfigFieldDescription_disable_typescript_base_config'), - javascript_file_extensions: getMessage('ConfigFieldDescription_javascript_file_extensions'), - typescript_file_extensions: getMessage('ConfigFieldDescription_typescript_file_extensions') - } -} - export type ESLintEngineConfig = { // Your project's main ESLint configuration file. May be provided as a path relative to the config_root. // If not supplied, and auto_discover_eslint_config=true, then Code Analyzer will attempt to find and apply it automatically. @@ -67,6 +53,52 @@ export const DEFAULT_CONFIG: ESLintEngineConfig = { config_root: process.cwd() // INTERNAL USE ONLY } +export const ESLINT_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { + overview: getMessage('ConfigOverview'), + fieldDescriptions: { + eslint_config_file: { + descriptionText: getMessage('ConfigFieldDescription_eslint_config_file'), + valueType: "string", + defaultValue: null + }, + eslint_ignore_file: { + descriptionText: getMessage('ConfigFieldDescription_eslint_ignore_file'), + valueType: "string", + defaultValue: null + }, + auto_discover_eslint_config: { + descriptionText: getMessage('ConfigFieldDescription_auto_discover_eslint_config'), + valueType: "boolean", + defaultValue: DEFAULT_CONFIG.auto_discover_eslint_config + }, + disable_javascript_base_config: { + descriptionText: getMessage('ConfigFieldDescription_disable_javascript_base_config'), + valueType: "boolean", + defaultValue: DEFAULT_CONFIG.disable_javascript_base_config + }, + disable_lwc_base_config: { + descriptionText: getMessage('ConfigFieldDescription_disable_lwc_base_config'), + valueType: "boolean", + defaultValue: DEFAULT_CONFIG.disable_lwc_base_config + }, + disable_typescript_base_config: { + descriptionText: getMessage('ConfigFieldDescription_disable_typescript_base_config'), + valueType: "boolean", + defaultValue: DEFAULT_CONFIG.disable_typescript_base_config + }, + javascript_file_extensions: { + descriptionText: getMessage('ConfigFieldDescription_javascript_file_extensions'), + valueType: "array", + defaultValue: DEFAULT_CONFIG.javascript_file_extensions + }, + typescript_file_extensions: { + descriptionText: getMessage('ConfigFieldDescription_typescript_file_extensions'), + valueType: "array", + defaultValue: DEFAULT_CONFIG.typescript_file_extensions + } + } +} + // See https://eslint.org/docs/v8.x/use/configure/configuration-files#configuration-file-formats export const LEGACY_ESLINT_CONFIG_FILES: string[] = ['.eslintrc.js', '.eslintrc.cjs', '.eslintrc.yaml', '.eslintrc.yml', '.eslintrc.json']; diff --git a/packages/code-analyzer-flowtest-engine/src/config.ts b/packages/code-analyzer-flowtest-engine/src/config.ts index e0bea7b5..f03eaccc 100644 --- a/packages/code-analyzer-flowtest-engine/src/config.ts +++ b/packages/code-analyzer-flowtest-engine/src/config.ts @@ -7,13 +7,6 @@ import {SemVer} from "semver"; const MINIMUM_PYTHON_VERSION = '3.10.0'; export const PYTHON_COMMAND = 'python_command'; -export const FLOWTEST_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { - overview: getMessage('ConfigOverview'), - fieldDescriptions: { - python_command: getMessage('ConfigFieldDescription_python_command') - } -} - export type FlowTestConfig = { // Indicates the specific Python command to use for the 'flowtest' engine. // May be provided as the name of a command that exists on the path, or an absolute file path location. @@ -22,6 +15,17 @@ export type FlowTestConfig = { python_command: string; } +export const FLOWTEST_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { + overview: getMessage('ConfigOverview'), + fieldDescriptions: { + python_command: { + descriptionText: getMessage('ConfigFieldDescription_python_command'), + valueType: "string", + defaultValue: null // Using null for doc and since it indicates that the value is calculated based on the environment + } + } +} + export async function validateAndNormalizeConfig(configValueExtractor: ConfigValueExtractor, pythonVersionIdentifier: PythonVersionIdentifier): Promise { const valueExtractor: FlowTestEngineConfigValueExtractor = new FlowTestEngineConfigValueExtractor( diff --git a/packages/code-analyzer-pmd-engine/src/config.ts b/packages/code-analyzer-pmd-engine/src/config.ts index b2028a3a..27ab29c3 100644 --- a/packages/code-analyzer-pmd-engine/src/config.ts +++ b/packages/code-analyzer-pmd-engine/src/config.ts @@ -12,26 +12,6 @@ export const CPD_AVAILABLE_LANGUAGES: string[] = Object.values(LanguageId); const DEFAULT_JAVA_COMMAND: string = 'java'; -export const PMD_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { - overview: getMessage('PmdConfigOverview'), - fieldDescriptions: { - java_command: getMessage('SharedConfigFieldDescription_java_command', PMD_ENGINE_NAME), - rule_languages: getMessage('PmdConfigFieldDescription_rule_languages', toAvailableLanguagesText(PMD_AVAILABLE_LANGUAGES)), - java_classpath_entries: getMessage('PmdConfigFieldDescription_java_classpath_entries'), - custom_rulesets: getMessage('PmdConfigFieldDescription_custom_rulesets') - } -} - -export const CPD_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { - overview: getMessage('CpdConfigOverview'), - fieldDescriptions: { - java_command: getMessage('SharedConfigFieldDescription_java_command', CPD_ENGINE_NAME), - rule_languages: getMessage('CpdConfigFieldDescription_rule_languages', toAvailableLanguagesText(CPD_AVAILABLE_LANGUAGES)), - minimum_tokens: getMessage('CpdConfigFieldDescription_minimum_tokens'), - skip_duplicate_files: getMessage('CpdConfigFieldDescription_skip_duplicate_files') - } -} - export type PmdEngineConfig = { // Indicates the specific "java" command to use for the 'pmd' engine. // May be provided as the name of a command that exists on the path, or an absolute file path location. @@ -67,6 +47,33 @@ export const DEFAULT_PMD_ENGINE_CONFIG: PmdEngineConfig = { custom_rulesets: [] } +export const PMD_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { + overview: getMessage('PmdConfigOverview'), + fieldDescriptions: { + java_command: { + descriptionText: getMessage('SharedConfigFieldDescription_java_command', PMD_ENGINE_NAME), + valueType: "string", + defaultValue: null // Using null for doc and since it indicates that the value is calculated based on the environment + }, + rule_languages: { + descriptionText: getMessage('PmdConfigFieldDescription_rule_languages', toAvailableLanguagesText(PMD_AVAILABLE_LANGUAGES)), + valueType: "array", + defaultValue: DEFAULT_PMD_ENGINE_CONFIG.rule_languages, + }, + java_classpath_entries: { + descriptionText: getMessage('PmdConfigFieldDescription_java_classpath_entries'), + valueType: "array", + defaultValue: DEFAULT_PMD_ENGINE_CONFIG.java_classpath_entries + }, + custom_rulesets: { + descriptionText: getMessage('PmdConfigFieldDescription_custom_rulesets'), + valueType: "array", + defaultValue: DEFAULT_PMD_ENGINE_CONFIG.custom_rulesets + } + } +} + + export type CpdEngineConfig = { // Indicates the specific "java" command to use for the 'cpd' engine. // May be provided as the name of a command that exists on the path, or an absolute file path location. @@ -95,6 +102,33 @@ export const DEFAULT_CPD_ENGINE_CONFIG: CpdEngineConfig = { skip_duplicate_files: false } +export const CPD_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { + overview: getMessage('CpdConfigOverview'), + fieldDescriptions: { + java_command: { + descriptionText: getMessage('SharedConfigFieldDescription_java_command', CPD_ENGINE_NAME), + valueType: "string", + defaultValue: null // Using null for doc and since it indicates that the value is calculated based on the environment + }, + rule_languages: { + descriptionText: getMessage('CpdConfigFieldDescription_rule_languages', toAvailableLanguagesText(CPD_AVAILABLE_LANGUAGES)), + valueType: "array", + defaultValue: DEFAULT_CPD_ENGINE_CONFIG.rule_languages + }, + minimum_tokens: { + descriptionText: getMessage('CpdConfigFieldDescription_minimum_tokens'), + valueType: "number", + defaultValue: DEFAULT_CPD_ENGINE_CONFIG.minimum_tokens, + }, + skip_duplicate_files: { + descriptionText: getMessage('CpdConfigFieldDescription_skip_duplicate_files'), + valueType: "boolean", + defaultValue: DEFAULT_CPD_ENGINE_CONFIG.skip_duplicate_files, + } + } +} + + export async function validateAndNormalizePmdConfig(configValueExtractor: ConfigValueExtractor, javaVersionIdentifier: JavaVersionIdentifier): Promise { const pmdConfigValueExtractor: PmdConfigValueExtractor = new PmdConfigValueExtractor(configValueExtractor, diff --git a/packages/code-analyzer-pmd-engine/test/plugin.test.ts b/packages/code-analyzer-pmd-engine/test/plugin.test.ts index df5a97ed..a0a8681e 100644 --- a/packages/code-analyzer-pmd-engine/test/plugin.test.ts +++ b/packages/code-analyzer-pmd-engine/test/plugin.test.ts @@ -40,7 +40,7 @@ describe('Tests for the PmdCpdEnginesPlugin', () => { expect(plugin.describeEngineConfig('pmd')).toEqual(PMD_ENGINE_CONFIG_DESCRIPTION); // Sanity check that we list the correct available languages: - expect(PMD_ENGINE_CONFIG_DESCRIPTION.fieldDescriptions!['rule_languages']).toEqual( + expect(PMD_ENGINE_CONFIG_DESCRIPTION.fieldDescriptions!['rule_languages'].descriptionText).toEqual( getMessage('PmdConfigFieldDescription_rule_languages', `'apex', 'html', 'javascript' (or 'ecmascript'), 'visualforce', 'xml'`)); }); diff --git a/packages/code-analyzer-regex-engine/src/config.ts b/packages/code-analyzer-regex-engine/src/config.ts index fa8b8f88..e2c4132b 100644 --- a/packages/code-analyzer-regex-engine/src/config.ts +++ b/packages/code-analyzer-regex-engine/src/config.ts @@ -7,17 +7,21 @@ import { import {getMessage} from "./messages"; import {convertToRegex} from "./utils"; +export type RegexEngineConfig = { + custom_rules: RegexRules +} + export const REGEX_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { overview: getMessage('ConfigOverview'), fieldDescriptions: { - custom_rules: getMessage('ConfigFieldDescription_custom_rules') + custom_rules: { + descriptionText: getMessage('ConfigFieldDescription_custom_rules'), + valueType: "object", + defaultValue: {} + } } } -export type RegexEngineConfig = { - custom_rules: RegexRules -} - export type RegexRules = { [ruleName: string] : RegexRule }