diff --git a/package.json b/package.json index 55db3b4ab3d..c5fed854af3 100644 --- a/package.json +++ b/package.json @@ -86,5 +86,6 @@ "packages": [ "packages/*" ] - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/packages/hint/src/lib/analyzer.ts b/packages/hint/src/lib/analyzer.ts index 2c667f33c6f..6c869935993 100644 --- a/packages/hint/src/lib/analyzer.ts +++ b/packages/hint/src/lib/analyzer.ts @@ -15,7 +15,7 @@ import { } from './types'; import { Engine } from './engine'; import { AnalyzerErrorStatus } from './enums/error-status'; -import { IFormatterConstructor } from './types/formatters'; + import { loadResources } from './utils/resource-loader'; import { logger, UserConfig } from '@hint/utils'; @@ -26,13 +26,7 @@ import { } from '@hint/utils-fs'; import { Problem } from '@hint/utils-types'; -const initFormatters = (formatters: IFormatterConstructor[]): IFormatter[] => { - const result = formatters.map((FormatterConstructor) => { - return new FormatterConstructor(); - }); - return result; -}; const validateResources = (resources: HintResources) => { if (resources.missing.length > 0 || resources.incompatible.length > 0) { @@ -59,11 +53,23 @@ const validateConnector = (configuration: Configuration) => { /** * Node API. */ +const normalizeFormatterConfigs = (formatters: CreateAnalyzerOptions['formatters'] = []) => { + return formatters.map((f) => + typeof f === 'string' + ? { name: f, options: {} } + : { name: f.name, options: f.options ?? {} } + ); +}; + export class Analyzer { private configuration: Configuration; private engine?: Engine; private _resources: HintResources; - private formatters: IFormatter[]; + //added this formatEntries + private formatterEntries!: Array<{ + formatter: IFormatter; + options: FormatterOptions; + }>; private watch: boolean | undefined; private messages: { [name: string]: string } = { 'fetch::end': '%url% downloaded', @@ -76,10 +82,11 @@ export class Analyzer { 'traverse::up': 'Traversing the DOM' } - private constructor(configuration: Configuration, resources: HintResources, formatters: IFormatter[]) { + + private constructor(configuration: Configuration, resources: HintResources) { this.configuration = configuration; this._resources = resources; - this.formatters = formatters; + this.watch = this.configuration.connector && this.configuration.connector.options && this.configuration.connector.options.watch; } @@ -88,29 +95,53 @@ export class Analyzer { * @param userConfiguration User configuration to load. * @param options Options used to initialize the configuration. */ - public static create(userConfiguration: UserConfig, options: CreateAnalyzerOptions = {}) { - let configuration: Configuration; - if (!userConfiguration) { - throw new AnalyzerError('Missed configuration', AnalyzerErrorStatus.ConfigurationError); - } - try { - configuration = Configuration.fromConfig(userConfiguration, options); - } catch (e) { - throw new AnalyzerError(`Invalid configuration. ${(e as Error).message}.`, AnalyzerErrorStatus.ConfigurationError); - } - const resources = loadResources(configuration!); - const formatters = initFormatters(resources.formatters); + public static create(userConfiguration: UserConfig, options: CreateAnalyzerOptions = {}) { + let configuration: Configuration; - validateResources(resources); - validateConnector(configuration); - validateHints(configuration); + if (!userConfiguration) { + throw new AnalyzerError('Missed configuration', AnalyzerErrorStatus.ConfigurationError); + } - return new Analyzer(configuration, resources, formatters); + try { + configuration = Configuration.fromConfig(userConfiguration, options); + } catch (e) { + throw new AnalyzerError(`Invalid configuration. ${(e as Error).message}.`, AnalyzerErrorStatus.ConfigurationError); } + const normalizedFormatterConfigs = normalizeFormatterConfigs(options.formatters); + + const resources = loadResources(configuration); + + + const formatterMap = new Map(); + + + const formatterEntries = normalizedFormatterConfigs.map(({ name, options }) => { + const formatter = formatterMap.get(name); + + if (!formatter) { + throw new AnalyzerError( + `Formatter '${name}' was not found.`, + AnalyzerErrorStatus.ConfigurationError + ); + } + + return { formatter, options }; + }); + + validateResources(resources); + validateConnector(configuration); + validateHints(configuration); + + const analyzer = new Analyzer(configuration, resources); + analyzer.formatterEntries = formatterEntries; + + return analyzer; +} + /** * Get the configuration file in a given directory. * @param filePath Config file or directory where to look for the config file. @@ -262,14 +293,17 @@ export class Analyzer { * @param {Problem[]} problems Problems to format. * @param {FormatterOptions} options Options for the formatters. */ - public async format(problems: Problem[], options: FormatterOptions = {}): Promise { - options.language = options.language || this.configuration.language; - options.resources = options.resources || this.resources; - - for (const formatter of this.formatters) { - await formatter.format(problems, options); - } + public async format(problems: Problem[], options: FormatterOptions = {}): Promise { + options.language = options.language || this.configuration.language; + options.resources = options.resources || this.resources; + + for (const entry of this.formatterEntries) { + await entry.formatter.format(problems, { + ...options, + ...entry.options + }); } +} /** * Close the engine if a scan is still in progress. diff --git a/packages/hint/src/lib/cli/analyze.ts b/packages/hint/src/lib/cli/analyze.ts index 00c3868a7dd..5f48e9ac338 100644 --- a/packages/hint/src/lib/cli/analyze.ts +++ b/packages/hint/src/lib/cli/analyze.ts @@ -235,8 +235,8 @@ const actionsToOptions = (actions: CLIOptions): CreateAnalyzerOptions => { const options: CreateAnalyzerOptions = { formatters: actions.formatters ? actions.formatters.split(',') : undefined, hints: actions.hints ? actions.hints.split(',') : undefined, - watch: actions.watch - }; + watch: actions.watch + }; return options; }; diff --git a/packages/hint/src/lib/config.ts b/packages/hint/src/lib/config.ts index 9c0d016b335..70eeeb26927 100644 --- a/packages/hint/src/lib/config.ts +++ b/packages/hint/src/lib/config.ts @@ -176,7 +176,9 @@ const updateConfigWithOptionsValues = (config: UserConfig, options: CreateAnalyz // If formatters are provided, use them if (options.formatters) { - config.formatters = options.formatters; + config.formatters = options.formatters?.map((f) => + typeof f === 'string' ? f : f.name +); debug(`Using formatters option provided from Analyzer options: ${options.formatters.join(', ')}`); } diff --git a/packages/hint/src/lib/types/analyzer.ts b/packages/hint/src/lib/types/analyzer.ts index 63bd0fe406e..abe517e8386 100644 --- a/packages/hint/src/lib/types/analyzer.ts +++ b/packages/hint/src/lib/types/analyzer.ts @@ -1,13 +1,20 @@ import { Problem } from '@hint/utils-types'; export type { UserConfig } from '@hint/utils'; +export type FormatterConfig = + | string + | { + name: string; + options?: Record; + }; export type CreateAnalyzerOptions = { - formatters?: string[]; + formatters?: FormatterConfig[]; hints?: string[]; watch?: boolean; } + export type Target = { url: string | URL; content?: string;