Skip to content

Commit

Permalink
chore(core): improve typings
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip committed Feb 14, 2022
1 parent 7fbae61 commit 2358b85
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 44 deletions.
24 changes: 11 additions & 13 deletions packages/core/src/ruleset/__tests__/ruleset.test.ts
Expand Up @@ -10,7 +10,7 @@ import { RulesetValidationError } from '../validation/index';
import { isPlainObject } from '@stoplight/json';
import { Format } from '../format';
import type { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema';
import { FormatsSet } from '../utils/formatsSet';
import { Formats } from '../formats';

async function loadRuleset(mod: Promise<{ default: RulesetDefinition }>, source?: string): Promise<Ruleset> {
return new Ruleset((await mod).default, { source });
Expand Down Expand Up @@ -1378,24 +1378,22 @@ describe('Ruleset', () => {
},
});

expect(ruleset.rules['valid-id'].getGivenForFormats(new FormatsSet([draft4]))).toStrictEqual(['$..id']);
expect(ruleset.rules['valid-id'].getGivenForFormats(new FormatsSet([draft6]))).toStrictEqual(['$..$id']);
expect(ruleset.rules['valid-id'].getGivenForFormats(new FormatsSet([draft7]))).toStrictEqual(['$..$id']);
expect(ruleset.rules['valid-id'].getGivenForFormats(new FormatsSet([draft6, draft7]))).toStrictEqual([
'$..$id',
]);
expect(ruleset.rules['valid-id'].getGivenForFormats(new Formats([draft4]))).toStrictEqual(['$..id']);
expect(ruleset.rules['valid-id'].getGivenForFormats(new Formats([draft6]))).toStrictEqual(['$..$id']);
expect(ruleset.rules['valid-id'].getGivenForFormats(new Formats([draft7]))).toStrictEqual(['$..$id']);
expect(ruleset.rules['valid-id'].getGivenForFormats(new Formats([draft6, draft7]))).toStrictEqual(['$..$id']);

expect(ruleset.rules['valid-parameter'].getGivenForFormats(new FormatsSet([oas2]))).toStrictEqual([
expect(ruleset.rules['valid-parameter'].getGivenForFormats(new Formats([oas2]))).toStrictEqual([
'$.parameters[*]',
'$.paths[*].parameters[?(@ && !@.$ref)]',
'$.paths[*][get,put,post,delete,options,head,patch,trace].parameters[?(@ && !@.$ref)]',
]);
expect(ruleset.rules['valid-parameter'].getGivenForFormats(new FormatsSet([oas3]))).toStrictEqual([
expect(ruleset.rules['valid-parameter'].getGivenForFormats(new Formats([oas3]))).toStrictEqual([
'$.components.parameters[*]',
'$.paths[*].parameters[?(@ && !@.$ref)]',
'$.paths[*][get,put,post,delete,options,head,patch,trace].parameters[?(@ && !@.$ref)]',
]);
expect(ruleset.rules['valid-parameter'].getGivenForFormats(new FormatsSet([oas2, oas3]))).toStrictEqual([
expect(ruleset.rules['valid-parameter'].getGivenForFormats(new Formats([oas2, oas3]))).toStrictEqual([
'$.components.parameters[*]',
'$.paths[*].parameters[?(@ && !@.$ref)]',
'$.paths[*][get,put,post,delete,options,head,patch,trace].parameters[?(@ && !@.$ref)]',
Expand Down Expand Up @@ -1442,7 +1440,7 @@ describe('Ruleset', () => {
},
});

expect(() => ruleset.rules['valid-header'].getGivenForFormats(new FormatsSet([oas3]))).toThrowError(
expect(() => ruleset.rules['valid-header'].getGivenForFormats(new Formats([oas3]))).toThrowError(
ReferenceError(
'Alias "HeaderObject" is circular. Resolution stack: HeaderObject -> HeaderObjects -> Components -> HeaderObject',
),
Expand Down Expand Up @@ -1479,8 +1477,8 @@ describe('Ruleset', () => {
},
});

expect(ruleset.rules['valid-id'].getGivenForFormats(new FormatsSet([draft7]))).toStrictEqual([]);
expect(ruleset.rules['valid-id'].getGivenForFormats(new FormatsSet([]))).toStrictEqual([]);
expect(ruleset.rules['valid-id'].getGivenForFormats(new Formats([draft7]))).toStrictEqual([]);
expect(ruleset.rules['valid-id'].getGivenForFormats(new Formats([]))).toStrictEqual([]);
});

it('should be serializable', () => {
Expand Down
@@ -1,10 +1,10 @@
import type { Format } from '../format';
import type { Format } from './format';

function printFormat(format: Format): string {
return format.displayName ?? format.name;
}

export class FormatsSet<T extends Format = Format> extends Set<T> {
export class Formats<T extends Format = Format> extends Set<T> {
public toJSON(): string[] {
return Array.from(this).map(printFormat);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/ruleset/index.ts
@@ -1,5 +1,5 @@
export { assertValidRuleset, RulesetValidationError } from './validation/index';
export { getDiagnosticSeverity } from './utils';
export { getDiagnosticSeverity } from './utils/severity';
export { createRulesetFunction, SchemaDefinition as RulesetFunctionSchemaDefinition } from './function';
export { Format } from './format';
export { RulesetDefinition, RuleDefinition, ParserOptions, HumanReadableDiagnosticSeverity } from './types';
Expand Down
13 changes: 7 additions & 6 deletions packages/core/src/ruleset/rule.ts
Expand Up @@ -13,9 +13,10 @@ import type {
RuleDefinition,
RulesetAliasesDefinition,
RulesetScopedAliasDefinition,
Stringifable,
} from './types';
import { minimatch } from './utils/minimatch';
import { FormatsSet } from './utils/formatsSet';
import { Formats } from './formats';
import { isSimpleAliasDefinition } from './utils/guards';

const ALIAS = /^#([A-Za-z0-9_-]+)/;
Expand All @@ -25,7 +26,7 @@ export interface IRule {
message: string | null;
severity: DiagnosticSeverity;
resolved: boolean;
formats: Set<Format> | null;
formats: Formats | null;
enabled: boolean;
recommended: boolean;
documentationUrl: string | null;
Expand All @@ -35,7 +36,7 @@ export interface IRule {

export type StringifiedRule = Omit<IRule, 'formats' | 'then'> & {
name: string;
formats: FormatsSet | null;
formats: string[] | null;
then: (Pick<IRuleThen, 'field'> & { function: string; functionOptions?: string })[];
owner: number;
};
Expand All @@ -45,7 +46,7 @@ export class Rule implements IRule {
public message: string | null;
#severity!: DiagnosticSeverity;
public resolved: boolean;
public formats: FormatsSet | null;
public formats: Formats | null;
#enabled: boolean;
public recommended: boolean;
public documentationUrl: string | null;
Expand All @@ -64,7 +65,7 @@ export class Rule implements IRule {
this.documentationUrl = definition.documentationUrl ?? null;
this.severity = definition.severity;
this.resolved = definition.resolved !== false;
this.formats = 'formats' in definition ? new FormatsSet(definition.formats) : null;
this.formats = 'formats' in definition ? new Formats(definition.formats) : null;
this.then = definition.then;
this.given = definition.given;
}
Expand Down Expand Up @@ -241,7 +242,7 @@ export class Rule implements IRule {
return new Rule(this.name, this.definition, this.owner);
}

public toJSON(): StringifiedRule {
public toJSON(): Stringifable<StringifiedRule> {
return {
name: this.name,
recommended: this.recommended,
Expand Down
25 changes: 11 additions & 14 deletions packages/core/src/ruleset/ruleset.ts
@@ -1,20 +1,21 @@
import { dirname, relative } from '@stoplight/path';
import { isPlainObject, extractPointerFromRef, extractSourceFromRef } from '@stoplight/json';
import { DiagnosticSeverity } from '@stoplight/types';
import { minimatch } from './utils/minimatch';
import { Rule, StringifiedRule } from './rule';
import {
import type {
FileRulesetSeverityDefinition,
ParserOptions,
RulesetAliasesDefinition,
RulesetDefinition,
RulesetOverridesDefinition,
Stringifable,
} from './types';
import { assertValidRuleset } from './validation/index';
import { mergeRule } from './mergers/rules';
import { DEFAULT_PARSER_OPTIONS, getDiagnosticSeverity } from '..';
import { mergeRulesets } from './mergers/rulesets';
import { isPlainObject, extractPointerFromRef, extractSourceFromRef } from '@stoplight/json';
import { DiagnosticSeverity } from '@stoplight/types';
import { FormatsSet } from './utils/formatsSet';
import { Formats } from './formats';
import { isSimpleAliasDefinition } from './utils/guards';

const STACK_SYMBOL = Symbol('@stoplight/spectral/ruleset/#stack');
Expand All @@ -33,15 +34,15 @@ export type StringifiedRuleset = {
extends: StringifiedRuleset[] | null;
source: string | null;
aliases: RulesetAliasesDefinition | null;
formats: FormatsSet | null;
formats: Formats | null;
rules: Record<string, StringifiedRule>;
overrides: RulesetOverridesDefinition | null;
parserOptions: ParserOptions;
};

export class Ruleset {
public readonly id = SEED++;
public readonly formats = new FormatsSet();
public readonly formats = new Formats();
public readonly overrides: RulesetOverridesDefinition | null;
public readonly aliases: RulesetAliasesDefinition | null;
public readonly hasComplexAliases: boolean;
Expand Down Expand Up @@ -84,7 +85,7 @@ export class Ruleset {
hasComplexAliases = true;

const targets = value.targets.map(target => ({
formats: new FormatsSet(target.formats),
formats: new Formats(target.formats),
given: target.given,
}));

Expand Down Expand Up @@ -265,10 +266,7 @@ export class Ruleset {
return ruleset;
}

public toJSON(): Omit<StringifiedRuleset, 'extends' | 'rules'> & {
extends: Ruleset['extends'];
rules: Ruleset['rules'];
} {
public toJSON(): Stringifable<StringifiedRuleset> {
return {
id: this.id,
extends: this.extends,
Expand Down Expand Up @@ -308,10 +306,9 @@ export class Ruleset {
this.formats.add(format);
}
} else if (rule.owner !== this) {
rule.formats =
rule.owner.definition.formats === void 0 ? null : new FormatsSet(rule.owner.definition.formats);
rule.formats = rule.owner.definition.formats === void 0 ? null : new Formats(rule.owner.definition.formats);
} else if (this.definition.formats !== void 0) {
rule.formats = new FormatsSet(this.definition.formats);
rule.formats = new Formats(this.definition.formats);
}

if (this.definition.documentationUrl !== void 0 && rule.documentationUrl === null) {
Expand Down
21 changes: 14 additions & 7 deletions packages/core/src/ruleset/types.ts
@@ -1,7 +1,7 @@
import { DiagnosticSeverity } from '@stoplight/types';
import { Format } from './format';
import { RulesetFunction, RulesetFunctionWithValidator } from '../types';
import { FormatsSet } from './utils/formatsSet';
import type { DiagnosticSeverity } from '@stoplight/types';
import type { Format } from './format';
import type { RulesetFunction, RulesetFunctionWithValidator } from '../types';
import type { Formats } from './formats';

export type HumanReadableDiagnosticSeverity = 'error' | 'warn' | 'info' | 'hint' | 'off';
export type FileRuleSeverityDefinition = DiagnosticSeverity | HumanReadableDiagnosticSeverity | boolean;
Expand All @@ -17,7 +17,7 @@ export type ParserOptions = {
export type RuleDefinition = {
type?: 'validation' | 'style';

formats?: Format[];
formats?: Formats | Format[];

documentationUrl?: string;

Expand Down Expand Up @@ -81,7 +81,7 @@ export type RulesetOverridesDefinition = ReadonlyArray<{ files: string[] } & Rul
export type RulesetScopedAliasDefinition = {
description?: string;
targets: {
formats: FormatsSet | Format[];
formats: Formats | Format[];
given: string[];
}[];
};
Expand All @@ -92,7 +92,7 @@ export type RulesetDefinition = Readonly<
{
documentationUrl?: string;
description?: string;
formats?: FormatsSet | Format[];
formats?: Formats | Format[];
parserOptions?: Partial<ParserOptions>;
overrides?: RulesetOverridesDefinition;
aliases?: RulesetAliasesDefinition;
Expand All @@ -112,3 +112,10 @@ export type RulesetDefinition = Readonly<
}
>
>;

// eslint-disable-next-line @typescript-eslint/ban-types
export type Stringifable<T> = T extends object
? {
[P in keyof T]: Stringifable<T[P]> | { toJSON?(): Stringifable<T[P]> };
}
: T;
1 change: 0 additions & 1 deletion packages/core/src/ruleset/utils/index.ts

This file was deleted.

0 comments on commit 2358b85

Please sign in to comment.