From f7b033152ac0888f084a8a4f8d620d92b565e037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Thu, 28 May 2020 06:04:34 +0200 Subject: [PATCH] perf: improve ruleset loading --- src/rulesets/mergers/formats.ts | 2 +- src/rulesets/mergers/functions.ts | 2 +- src/rulesets/mergers/rules.ts | 27 +++++++++++++++++++-------- src/rulesets/reader.ts | 15 ++++++++++++--- src/rulesets/validation.ts | 2 +- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/rulesets/mergers/formats.ts b/src/rulesets/mergers/formats.ts index 5c7abd70f..a9c3ac3fa 100644 --- a/src/rulesets/mergers/formats.ts +++ b/src/rulesets/mergers/formats.ts @@ -1,6 +1,6 @@ import { RuleCollection } from '../../types'; -export function mergeFormats(rules: RuleCollection, formats: string[]) { +export function mergeFormats(rules: RuleCollection, formats: string[]): void { if (formats.length > 0) { for (const rule of Object.values(rules)) { if (typeof rule === 'object' && rule.formats === void 0) { diff --git a/src/rulesets/mergers/functions.ts b/src/rulesets/mergers/functions.ts index e85f9401c..22cdf2733 100644 --- a/src/rulesets/mergers/functions.ts +++ b/src/rulesets/mergers/functions.ts @@ -8,7 +8,7 @@ export function mergeFunctions( target: RulesetFunctionCollection, source: RulesetFunctionCollection, rules: RuleCollection, -) { +): void { const map: Dictionary = {}; for (const [name, def] of Object.entries(source)) { diff --git a/src/rulesets/mergers/rules.ts b/src/rulesets/mergers/rules.ts index adfe8e40f..bd95bd084 100644 --- a/src/rulesets/mergers/rules.ts +++ b/src/rulesets/mergers/rules.ts @@ -1,6 +1,5 @@ import { DiagnosticSeverity } from '@stoplight/types'; -import { cloneDeep } from 'lodash'; -import { HumanReadableDiagnosticSeverity, IRule } from '../../types'; +import { HumanReadableDiagnosticSeverity, IRule, IThen } from '../../types'; import { FileRule, FileRuleCollection, FileRulesetSeverity } from '../../types/ruleset'; import { DEFAULT_SEVERITY_LEVEL, getDiagnosticSeverity, getSeverityLevel } from '../severity'; import { isValidRule } from '../validation'; @@ -37,7 +36,7 @@ export function mergeRules( const ROOT_DESCRIPTOR = Symbol('root-descriptor'); -function markRule(rule: IRule) { +function markRule(rule: IRule): void { if (!(ROOT_DESCRIPTOR in rule)) { Object.defineProperty(rule, ROOT_DESCRIPTOR, { configurable: false, @@ -48,7 +47,7 @@ function markRule(rule: IRule) { } } -function updateRootRule(root: IRule, newRule: IRule | null) { +function updateRootRule(root: IRule, newRule: IRule | null): void { markRule(root); Object.assign(root[ROOT_DESCRIPTOR], copyRule(newRule === null ? root : Object.assign(root, newRule))); } @@ -57,11 +56,23 @@ function getRootRule(rule: IRule): IRule | null { return rule[ROOT_DESCRIPTOR] !== undefined ? rule[ROOT_DESCRIPTOR] : null; } -function copyRule(rule: IRule) { - return cloneDeep(rule); +function copyRuleThen(then: IThen): IThen { + return { + ...then, + ...('functionOptions' in then ? { ...then.functionOptions } : null), + }; } -function processRule(rules: FileRuleCollection, name: string, rule: FileRule | FileRulesetSeverity) { +function copyRule(rule: IRule): IRule { + return { + ...rule, + ...('then' in rule + ? { then: Array.isArray(rule.then) ? rule.then.map(copyRuleThen) : copyRuleThen(rule.then) } + : null), + }; +} + +function processRule(rules: FileRuleCollection, name: string, rule: FileRule | FileRulesetSeverity): void { const existingRule = rules[name]; switch (typeof rule) { @@ -116,7 +127,7 @@ function processRule(rules: FileRuleCollection, name: string, rule: FileRule | F } } -function normalizeRule(rule: IRule, severity: DiagnosticSeverity | HumanReadableDiagnosticSeverity | undefined) { +function normalizeRule(rule: IRule, severity: DiagnosticSeverity | HumanReadableDiagnosticSeverity | undefined): void { if (rule.recommended === void 0) { rule.recommended = true; } diff --git a/src/rulesets/reader.ts b/src/rulesets/reader.ts index de829d0f2..c6e4126bb 100644 --- a/src/rulesets/reader.ts +++ b/src/rulesets/reader.ts @@ -1,6 +1,6 @@ import { Cache } from '@stoplight/json-ref-resolver'; import { ICache } from '@stoplight/json-ref-resolver/types'; -import { join } from '@stoplight/path'; +import { extname, join } from '@stoplight/path'; import { Optional } from '@stoplight/types'; import { parse } from '@stoplight/yaml'; import { readFile, readParsable } from '../fs/reader'; @@ -15,6 +15,14 @@ export interface IRulesetReadOptions { timeout?: number; } +function parseContent(content: string, source: string): unknown { + if (extname(source) === '.json') { + return JSON.parse(content); + } + + return parse(content); +} + export async function readRuleset(uris: string | string[], opts?: IRulesetReadOptions): Promise { const base: IRuleset = { rules: {}, @@ -54,18 +62,19 @@ const createRulesetProcessor = ( processedRulesets.add(rulesetUri); const { result } = await httpAndFileResolver.resolve( - parse( + parseContent( await readParsable(rulesetUri, { timeout: readOpts?.timeout, encoding: 'utf8', }), + rulesetUri, ), { baseUri: rulesetUri, dereferenceInline: false, uriCache, async parseResolveResult(opts) { - opts.result = parse(opts.result); + opts.result = parseContent(opts.result, opts.targetAuthority.pathname()); return opts; }, }, diff --git a/src/rulesets/validation.ts b/src/rulesets/validation.ts index 3f874001c..9c7c37ff3 100644 --- a/src/rulesets/validation.ts +++ b/src/rulesets/validation.ts @@ -9,7 +9,7 @@ import { IFunction, IFunctionPaths, IFunctionValues, IRule, JSONSchema } from '. const ajv = new AJV({ allErrors: true, jsonPointers: true }); const validate = ajv.addSchema(ruleSchema).compile(rulesetSchema); -const serializeAJVErrors = (errors: AJV.ErrorObject[]) => +const serializeAJVErrors = (errors: AJV.ErrorObject[]): string => errors.map(({ message, dataPath }) => `${dataPath} ${message}`).join('\n'); export class ValidationError extends AJV.ValidationError {