Skip to content

Commit

Permalink
feat: types with intellisense (eslint-types#152)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimava committed Mar 24, 2023
1 parent b333fd8 commit 0a183db
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 20 deletions.
47 changes: 35 additions & 12 deletions scripts/generate-rule-files/src/rule-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,19 @@ export class RuleFile {
.output();
}

private readonly ruleSchemas: {
Setting?: string;
Config?: string;
Option?: string;
Partial: string[];
} = { Partial: [] };

/**
* Generate a type from a JSON schema and append it to the file content.
*/
private async appendJsonSchemaType(
schema: JSONSchema4,
comment: string,
comment: 'Setting' | 'Config' | 'Option',
): Promise<void> {
const type: string = await generateTypeFromSchema(
schema,
Expand All @@ -109,6 +116,12 @@ export class RuleFile {
const jsdoc: string = JsDocBuilder.build().add(`${comment}.`).output();
this.content += `\n${jsdoc}`;
this.content += `\n${type}\n`;
const fullSchema: string = type
.replace(/export (type|interface) \w+\s*(=\s*)?/, '')
.trim();
const [mainSchema, ...parts] = fullSchema.split('\nexport ');
this.ruleSchemas[comment] = mainSchema;
this.ruleSchemas.Partial.push(...parts);
}

/**
Expand Down Expand Up @@ -173,12 +186,26 @@ export class RuleFile {
rulePrefix = '';
}

this.content += this.generateTypeJsDoc() + '\n';
const nestedDepth: number = this.ruleName.split('/').length;
const ruleConfigImportPath: string = `${'../'.repeat(
nestedDepth,
)}rule-config`;

this.content += `export interface ${ruleName}Rule {`;
this.content += `${this.generateTypeJsDoc()}\n`;
this.content += `'${rulePrefix}${this.ruleName}': ${ruleName}RuleConfig;`;
this.content += '}';
const ruleSchema: string[] = [
'RuleLevel',
this.ruleSchemas.Option,
this.ruleSchemas.Config,
this.ruleSchemas.Setting,
].filter((e): e is string => !!e);

this.content += `
import { Rule, RuleLevel } from '${ruleConfigImportPath}';
export type ${ruleName}Rule = {
${this.generateTypeJsDoc()}
'${rulePrefix}${this.ruleName}': Rule<[${ruleSchema.join(',\n')}]>
}
${this.ruleSchemas.Partial.join('\n')}
`;
}

/**
Expand All @@ -195,14 +222,10 @@ export class RuleFile {
* Generate a file with the rule typings.
*/
public async generate(): Promise<string> {
this.appendRuleConfigImport();
// for this.ruleSchemas side effects
await this.appendRuleSchemaTypes();

if (this.mainSchema) {
this.appendRuleOptions();
}

this.appendRuleConfig();
this.content = '';
this.appendRule();

this.content = format(this.content);
Expand Down
39 changes: 39 additions & 0 deletions src/rules/experimental.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export type Severity = 0 | 1 | 2;
export type RuleLevel = Severity | 'off' | 'warn' | 'error';

type Rule<ARGS extends [RuleLevel, ...any[]]> =
| (ARGS extends [RuleLevel]
? [RuleLevel]
: ARGS extends [RuleLevel, infer A]
? [RuleLevel, A?]
: ARGS extends [RuleLevel, infer A, infer B]
? [RuleLevel, A?, B?]
: ARGS extends [RuleLevel, infer A, infer B, infer C]
? [RuleLevel, A?, B?, C?]
: never)
| RuleLevel;

export type ARule = {
'a-rule': Rule<[RuleLevel]>;
// ^?
// (property) 'a-rule': Rule<[RuleLevel]>
};
export type BRule = {
'b-rule': Rule<[RuleLevel, 'a' | 'b']>;
// ^?
// (property) 'b-rule': Rule<[RuleLevel, "a" | "b"]>
};
export type CRule = {
'c-rule': Rule<[RuleLevel, 'a' | 'b', { optional: boolean }]>;
// ^?
// (property) 'c-rule': Rule<[RuleLevel, "a" | "b", { optional: boolean; }]>
};
export type DRule = {
'd-rule': Rule<
// ^?
// (property) 'd-rule': Rule<[RuleLevel, "a" | "b", { optional: boolean; }, { extended: boolean; }]>
[RuleLevel, 'a' | 'b', { optional: boolean }, { extended: boolean }]
>;
};

export type IntellisenceRules = ARule & BRule & CRule & DRule;
15 changes: 7 additions & 8 deletions src/rules/rule-config.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { RuleLevel } from './rule-severity';
export type { RuleLevel } from './rule-severity';

// Synced to https://github.com/DefinitelyTyped/DefinitelyTyped/blob/042141ce5f77f36df01c344ad09f32feda26c4fd/types/eslint/helpers.d.ts#L1-L3

Expand All @@ -19,13 +20,11 @@ export type RuleLevelAndOptions<Options extends any[] = any[]> = Prepend<
RuleLevel
>;

export type RuleEntry<Options extends any[] = any[]> =
export type Rule<Options extends [RuleLevel, ...any[]]> = //
Options extends [RuleLevel, ...infer Options]
? RuleLevelAndOptions<Options>
: never;

export type RuleConfig<Options extends any[] = any[]> =
| RuleLevel
| RuleLevelAndOptions<Options>;

/**
* Rule configuration.
*
* @alias RuleEntry
*/
export type RuleConfig<Options extends any[] = any[]> = RuleEntry<Options>;

0 comments on commit 0a183db

Please sign in to comment.