Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ export interface RulesTypeGenOptions {
*/
includeAugmentation?: boolean

/**
* Include JSDoc comments for rule options.
*
* @default false
*/
includeRuleOptionsJsDoc?: boolean

/**
* Augment the `DefaultConfigNamesMap` interface for `eslint-flat-config-utils`
* For auto-completion of config names etc.
Expand Down Expand Up @@ -99,6 +106,7 @@ export async function pluginsToRulesDTS(
includeTypeImports = true,
includeIgnoreComments = true,
includeAugmentation = true,
includeRuleOptionsJsDoc = false,
augmentFlatConfigUtils = false,
exportTypeName = 'RuleOptions',
compileOptions = {},
Expand All @@ -122,7 +130,7 @@ export async function pluginsToRulesDTS(

rules.sort(([a], [b]) => a.localeCompare(b))
const resolved = await Promise.all(rules
.map(([name, rule]) => compileRule(name, rule, compileOptions)))
.map(([name, rule]) => compileRule(name, rule, compileOptions, { includeRuleOptionsJsDoc })))

const exports = [
...(includeIgnoreComments
Expand Down Expand Up @@ -181,6 +189,7 @@ export async function compileRule(
ruleName: string,
rule: Rule.RuleModule,
compileOptions: Partial<CompileOptions> = {},
typeGenOptions: RulesTypeGenOptions = {},
) {
const meta = rule.meta ?? {}
let schemas = meta.schema as JSONSchema4[] ?? []
Expand Down Expand Up @@ -237,10 +246,11 @@ export async function compileRule(
},
...compileOptions,
})
lines.push(
compiled
.replace(/\/\*[\s\S]*?\*\//g, ''),
)

if (typeGenOptions?.includeRuleOptionsJsDoc)
lines.push(compiled)
else
lines.push(compiled.replace(/\/\*[\s\S]*?\*\//g, ''))
}
catch (error) {
console.warn(`Failed to compile schema ${ruleName} for rule ${ruleName}. Falling back to unknown.`)
Expand Down
10 changes: 10 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { expect, it } from 'vitest'
import { flatConfigsToRulesDTS, pluginsToRulesDTS } from '../src/core'
import { invalidJsonSchemaPlugin } from './input/invalid-json-schema-plugin'
import { pluginWithSchemaIds } from './input/plugin-with-schema-ids'
import { ruleWithDescriptionInJsdoc } from './input/rule-with-description-in-jsdoc'

it('pluginsToRuleOptions', async () => {
await expect(await pluginsToRulesDTS({
Expand Down Expand Up @@ -43,6 +44,15 @@ it('json schema with ids', async () => {
.toMatchFileSnapshot('./output/plugin-with-schema-ids.d.ts')
})

it('rule with description in jsdoc', async () => {
await expect(await pluginsToRulesDTS({
plugin: ruleWithDescriptionInJsdoc,
}, {
includeRuleOptionsJsDoc: true,
}))
.toMatchFileSnapshot('./output/rule-with-description-in-jsdoc.d.ts')
})

it('flatConfigsToRuleOptions', async () => {
await expect(await flatConfigsToRulesDTS(await vue() as any))
.toMatchFileSnapshot('./output/flat-config-vue.d.ts')
Expand Down
30 changes: 30 additions & 0 deletions test/input/rule-with-description-in-jsdoc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { ESLint, Rule } from 'eslint'

export const ruleWithDescriptionInJsdoc: ESLint.Plugin = {
rules: {
'rule-with-description-in-properties-schema': {
create: () => null as unknown as Rule.RuleListener,
meta: {
schema: {
id: 'schemaId',
type: 'object',
additionalProperties: false,
properties: {
a: {
id: 'aId',
type: 'boolean',
default: false,
description: 'This is a boolean property',
},
b: {
id: 'bId',
type: 'string',
default: 'default',
description: 'This is a string property',
},
},
},
},
},
},
}
28 changes: 28 additions & 0 deletions test/output/rule-with-description-in-jsdoc.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable */
/* prettier-ignore */
import type { Linter } from 'eslint'

declare module 'eslint' {
namespace Linter {
interface RulesRecord extends RuleOptions {}
}
}

export interface RuleOptions {
'plugin/rule-with-description-in-properties-schema'?: Linter.RuleEntry<_PluginRuleWithDescriptionInPropertiesSchemaSchemaId>
}

/* ======= Declarations ======= */
// ----- plugin/rule-with-description-in-properties-schema -----
/**
* This is a boolean property
*/
type _PluginRuleWithDescriptionInPropertiesSchemaAId = boolean
/**
* This is a string property
*/
type _PluginRuleWithDescriptionInPropertiesSchemaBId = string
interface _PluginRuleWithDescriptionInPropertiesSchemaSchemaId {
a?: _PluginRuleWithDescriptionInPropertiesSchemaAId
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the jsdocs should be here to make it effective on the userland, no?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I think that too, but the snapshot in the tests places it there, and all tests will pass, it's strange but works xD

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, snapshot only tests changes, but not correctness. If snapshot shows that, it means the implementation should be updated

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good point, I'll try out how to fix this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently, the json-schema-to-typescript-lite package

in the generateInterface and generateStandaloneType functions

the jsdocs is conditioned by ast.standaloneName if has standaloneName the jsdoc is placed in the top level type definition.

It can be fixed by adding an option to prefer the jsdoc of the options inside the interface instead of the top level type, (I can send a pr for this), or it can be left as is, and used in this way, because the original package also has this behavior (bcherny/json-schema-to-typescript), generateInterface and generateStandaloneType functions

b?: _PluginRuleWithDescriptionInPropertiesSchemaBId
}