diff --git a/src/__tests__/spectral.test.ts b/src/__tests__/spectral.test.ts index 6ef70b06e..a7693515d 100644 --- a/src/__tests__/spectral.test.ts +++ b/src/__tests__/spectral.test.ts @@ -51,6 +51,129 @@ describe('spectral', () => { expect(expectedCustomRuleSet).toEqual(givenCustomRuleSet); }); + // Assures: https://stoplightio.atlassian.net/browse/SL-789 + test('setting rules with opts should update/append the current ruleset', () => { + const ruleset = { + rules: { + format: { + rule1: { + type: RuleType.STYLE, + function: RuleFunction.TRUTHY, + path: '$', + enabled: true, + summary: '', + input: { + properties: 'something', + }, + }, + }, + }, + }; + // deep copy + const s = new Spectral({ rulesets: [ruleset] }); + + s.setRules([ + { + rules: { + differentFormat: { + rule2: { + type: RuleType.STYLE, + function: RuleFunction.TRUTHY, + path: '$', + enabled: true, + summary: '', + input: { + properties: 'a different rule', + }, + }, + }, + }, + }, + ]); + + expect(s.getRules()).toHaveLength(2); + + s.setRules( + [ + { + rules: { + format: { + rule1: false, + }, + }, + }, + ], + { includeCurrent: true } + ); + + expect(s.getRules()).toHaveLength(2); + }); + + // Assures: https://stoplightio.atlassian.net/browse/SL-789 + test('setting rules without includeCurren opts should overwrite the current ruleset', () => { + const ruleset = { + rules: { + format: { + rule1: { + type: RuleType.STYLE, + function: RuleFunction.TRUTHY, + path: '$', + enabled: true, + summary: '', + input: { + properties: 'something', + }, + }, + }, + }, + }; + // deep copy + const s = new Spectral({ rulesets: [ruleset] }); + + s.setRules( + [ + { + rules: { + differentFormat: { + rule2: { + type: RuleType.STYLE, + function: RuleFunction.TRUTHY, + path: '$', + enabled: true, + summary: '', + input: { + properties: 'a different rule', + }, + }, + }, + }, + }, + ], + { includeCurrent: false } + ); + + expect(s.getRules()).toHaveLength(1); + expect(s.getRules()).toMatchInlineSnapshot(` +Array [ + Object { + "apply": [Function], + "format": "differentFormat", + "name": "rule2", + "rule": Object { + "enabled": true, + "function": "truthy", + "input": Object { + "properties": "a different rule", + }, + "path": "$", + "summary": "", + "type": "style", + }, + }, +] +`); + }); + // Assures: https://stoplightio.atlassian.net/browse/SL-787 test('given a ruleset with two identical rules under two distinct formats should not collide', () => { const rulesets = [ diff --git a/src/index.ts b/src/index.ts index d9225628a..b196c7822 100644 --- a/src/index.ts +++ b/src/index.ts @@ -67,7 +67,7 @@ export class Spectral { // @ts-ignore private _rulesets: types.IRuleset[] = []; - private _functions: IFunctionStore = {}; + private _functions: IFunctionStore = defaultFunctions; constructor(opts: ISpectralOpts) { this.setRules(opts.rulesets); @@ -89,21 +89,23 @@ export class Spectral { return rules; } - public setRules(rulesets: types.IRuleset[]) { - this._rulesets = merge([], rulesets); - this._functions = { ...defaultFunctions, ...this._rulesetsToFunctions(this._rulesets) }; - this._rulesByIndex = this._rulesetsToRules(this._rulesets, this._rulesByIndex, this._functions); + public setRules( + rulesets: types.IRuleset[], + opts: { includeCurrent: boolean } = { includeCurrent: true } + ) { + const { rulesets: rSets, functionStore, ruleStore } = this._parseRuleSets(rulesets, opts); + + this._rulesets = rSets; + this._functions = functionStore; + this._rulesByIndex = ruleStore; } public run(opts: IRunOpts): types.IRuleResult[] { const { target, rulesets = [] } = opts; - let ruleStore = this._rulesByIndex; - - if (rulesets.length) { - const functionStore = { ...this._functions, ...this._rulesetsToFunctions(rulesets) }; - ruleStore = this._rulesetsToRules(rulesets, ruleStore, functionStore); - } + const ruleStore = rulesets.length + ? this._parseRuleSets(rulesets, { includeCurrent: true }).ruleStore + : this._rulesByIndex; return target ? this.runAllLinters(ruleStore, opts) : []; } @@ -181,6 +183,27 @@ export class Spectral { return ruleEntry.apply(opt); } + private _parseRuleSets( + rulesets: types.IRuleset[], + opts: { includeCurrent: boolean } = { includeCurrent: true } + ): { rulesets: types.IRuleset[]; functionStore: IFunctionStore; ruleStore: IRuleStore } { + const rSets = merge([], rulesets); + + let functionStore = opts.includeCurrent ? this._functions : defaultFunctions; + let ruleStore = opts.includeCurrent ? this._rulesByIndex : {}; + + if (rSets.length) { + functionStore = { ...functionStore, ...this._rulesetsToFunctions(rulesets) }; + ruleStore = this._rulesetsToRules(rulesets, ruleStore, functionStore); + } + + return { + rulesets: rSets, + functionStore, + ruleStore, + }; + } + private _parseRuleDefinition( { name, format, rule }: { name: string; format: string; rule: types.Rule }, functionStore: IFunctionStore = {}