From 601a611da114c5ded72f58b95ce9e999390e73bd Mon Sep 17 00:00:00 2001 From: Bryan Mishkin <698306+bmish@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:12:51 -0400 Subject: [PATCH] Support auto-generated rule options lists (#481) --- README.md | 30 +++- .../eslint-plugin-test/docs/rules/no-foo.md | 33 +++- lib/comment-markers.ts | 6 + lib/generator.ts | 8 +- lib/rule-options-list.ts | 150 ++++++++++++++++ lib/rule-options.ts | 39 +++- .../rule-options-list-test.ts.snap | 38 ++++ test/lib/generate/rule-options-list-test.ts | 166 ++++++++++++++++++ test/lib/rule-options-test.ts | 144 +++++++++++++-- 9 files changed, 593 insertions(+), 21 deletions(-) create mode 100644 lib/rule-options-list.ts create mode 100644 test/lib/generate/__snapshots__/rule-options-list-test.ts.snap create mode 100644 test/lib/generate/rule-options-list-test.ts diff --git a/README.md b/README.md index 03b508a5..2be18879 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Generates the following documentation covering a [wide variety](#column-and-noti - `README.md` rules table - `README.md` configs table - Rule doc titles and notices +- Rule doc options lists Also performs [configurable](#configuration-options) section consistency checks on rule docs: @@ -18,11 +19,16 @@ Also performs [configurable](#configuration-options) section consistency checks - [Motivation](#motivation) - [Setup](#setup) + - [Scripts](#scripts) + - [Update `README.md`](#update-readmemd) + - [Update rule docs](#update-rule-docs) + - [Configure linting](#configure-linting) - [Usage](#usage) - [Examples](#examples) - [Rules list table](#rules-list-table) - [Configs list table](#configs-list-table) - [Rule doc notices](#rule-doc-notices) + - [Rule doc options lists](#rule-doc-options-lists) - [Users](#users) - [Configuration options](#configuration-options) - [Column and notice types](#column-and-notice-types) @@ -52,6 +58,8 @@ Install it: npm i --save-dev eslint-doc-generator ``` +### Scripts + Add scripts to `package.json`: - Both a lint script to ensure everything is up-to-date in CI and an update script for contributors to run locally @@ -70,6 +78,8 @@ Add scripts to `package.json`: } ``` +### Update `README.md` + Delete any old rules list from your `README.md`. A new one will be automatically added to your `## Rules` section (along with the following marker comments if they don't already exist): ```md @@ -77,23 +87,37 @@ Delete any old rules list from your `README.md`. A new one will be automatically ``` -Optionally, add these marker comments to your `README.md` where you would like the configs list to go (uses the `description` property exported by each config if available): +Optionally, add these marker comments to your `README.md` in a `## Configs` section or similar location (uses the `description` property exported by each config if available): ```md ``` +### Update rule docs + Delete any old recommended/fixable/etc. notices from your rule docs. A new title and notices will be automatically added to the top of each rule doc (along with a marker comment if it doesn't already exist). ```md ``` +Optionally, add these marker comments to your rule docs in an `## Options` section or similar location: + +```md + + +``` + +Note that rule option lists are subject-to-change as we add support for more kinds and properties of schemas. To fully take advantage of them, you'll want to ensure your rules have the `meta.schema` property fleshed out with properties like `description`, `type`, `enum`, `default`, `required`, `deprecated`. + +### Configure linting + And be sure to enable the `recommended` rules from [eslint-plugin-eslint-plugin](https://github.com/eslint-community/eslint-plugin-eslint-plugin) as well as: - [eslint-plugin/require-meta-docs-description](https://github.com/eslint-community/eslint-plugin-eslint-plugin/blob/main/docs/rules/require-meta-docs-description.md) to ensure your rules have consistent descriptions for use in the generated docs - [eslint-plugin/require-meta-docs-url](https://github.com/eslint-community/eslint-plugin-eslint-plugin/blob/main/docs/rules/require-meta-docs-url.md) to ensure your rule docs are linked to by editors on highlighted violations +- [eslint-plugin/require-meta-schema](https://github.com/eslint-community/eslint-plugin-eslint-plugin/blob/main/docs/rules/require-meta-schema.md) to ensure your rules have schemas for use in determining options ## Usage @@ -119,6 +143,10 @@ See the generated configs table in our example [`README.md`](./docs/examples/esl See the generated rule doc title and notices in our example rule docs [`no-foo.md`](./docs/examples/eslint-plugin-test/docs/rules/no-foo.md), [`prefer-bar.md`](./docs/examples/eslint-plugin-test/docs/rules/prefer-bar.md), [`require-baz.md`](./docs/examples/eslint-plugin-test/docs/rules/require-baz.md). +### Rule doc options lists + +See the generated rule doc options lists in our example rule doc [`no-foo.md`](./docs/examples/eslint-plugin-test/docs/rules/no-foo.md). + ### Users This tool is used by popular ESLint plugins like: diff --git a/docs/examples/eslint-plugin-test/docs/rules/no-foo.md b/docs/examples/eslint-plugin-test/docs/rules/no-foo.md index a4edf8aa..b909860b 100644 --- a/docs/examples/eslint-plugin-test/docs/rules/no-foo.md +++ b/docs/examples/eslint-plugin-test/docs/rules/no-foo.md @@ -22,4 +22,35 @@ Examples would normally go here. ## Options -Config options would normally go here. + + +| Name | Description | Type | Choices | Default | Required | Deprecated | +| :---- | :---------------------------- | :------ | :---------------- | :------- | :------- | :--------- | +| `bar` | Choose how to use the rule. | String | `always`, `never` | `always` | Yes | | +| `foo` | Enable some kind of behavior. | Boolean | | `false` | | Yes | + + + +For the purpose of this example, below is the `meta.schema` that would generate the above rule options table: + +```json +[{ + "type": "object", + "properties": { + "foo": { + "type": "boolean", + "description": "Enable some kind of behavior.", + "deprecated": true, + "default": false + }, + "bar": { + "description": "Choose how to use the rule.", + "type": "string", + "enum": ["always", "never"], + "default": "always" + } + }, + "required": ["bar"], + "additionalProperties": false +}] +``` diff --git a/lib/comment-markers.ts b/lib/comment-markers.ts index 1d23b547..3e367374 100644 --- a/lib/comment-markers.ts +++ b/lib/comment-markers.ts @@ -11,3 +11,9 @@ export const BEGIN_CONFIG_LIST_MARKER = ''; export const END_CONFIG_LIST_MARKER = ''; + +// Markers so that the rule options table list can be automatically updated. +export const BEGIN_RULE_OPTIONS_LIST_MARKER = + ''; +export const END_RULE_OPTIONS_LIST_MARKER = + ''; diff --git a/lib/generator.ts b/lib/generator.ts index 4a235997..46b915a2 100644 --- a/lib/generator.ts +++ b/lib/generator.ts @@ -27,6 +27,7 @@ import { diff } from 'jest-diff'; import type { GenerateOptions } from './types.js'; import { OPTION_TYPE, RuleModule } from './types.js'; import { replaceRulePlaceholder } from './rule-link.js'; +import { updateRuleOptionsList } from './rule-options-list.js'; function stringOrArrayWithFallback( stringOrArray: undefined | T, @@ -180,7 +181,10 @@ export async function generate(path: string, options?: GenerateOptions) { const contents = readFileSync(pathToDoc).toString(); const contentsNew = await postprocess( - replaceOrCreateHeader(contents, newHeaderLines, END_RULE_HEADER_MARKER), + updateRuleOptionsList( + replaceOrCreateHeader(contents, newHeaderLines, END_RULE_HEADER_MARKER), + rule + ), resolve(pathToDoc) ); @@ -229,7 +233,7 @@ export async function generate(path: string, options?: GenerateOptions) { ['Options', 'Config'], hasOptions(schema) ); - for (const namedOption of getAllNamedOptions(schema)) { + for (const { name: namedOption } of getAllNamedOptions(schema)) { expectContentOrFail( `\`${name}\` rule doc`, 'rule option', diff --git a/lib/rule-options-list.ts b/lib/rule-options-list.ts new file mode 100644 index 00000000..533fecef --- /dev/null +++ b/lib/rule-options-list.ts @@ -0,0 +1,150 @@ +import { + BEGIN_RULE_OPTIONS_LIST_MARKER, + END_RULE_OPTIONS_LIST_MARKER, +} from './comment-markers.js'; +import { markdownTable } from 'markdown-table'; +import type { RuleModule } from './types.js'; +import { RuleOption, getAllNamedOptions } from './rule-options.js'; +import { capitalizeOnlyFirstLetter } from './string.js'; + +export enum COLUMN_TYPE { + // Alphabetical order. + DEFAULT = 'default', + DEPRECATED = 'deprecated', + DESCRIPTION = 'description', + ENUM = 'enum', + NAME = 'name', + REQUIRED = 'required', + TYPE = 'type', +} + +const HEADERS: { + [key in COLUMN_TYPE]: string; +} = { + // Alphabetical order. + [COLUMN_TYPE.DEFAULT]: 'Default', + [COLUMN_TYPE.DEPRECATED]: 'Deprecated', + [COLUMN_TYPE.DESCRIPTION]: 'Description', + [COLUMN_TYPE.ENUM]: 'Choices', + [COLUMN_TYPE.NAME]: 'Name', + [COLUMN_TYPE.REQUIRED]: 'Required', + [COLUMN_TYPE.TYPE]: 'Type', +}; + +const COLUMN_TYPE_DEFAULT_PRESENCE_AND_ORDERING: { + [key in COLUMN_TYPE]: boolean; +} = { + // Object keys ordered in display order. + // Object values indicate whether the column is displayed by default. + [COLUMN_TYPE.NAME]: true, + [COLUMN_TYPE.DESCRIPTION]: true, + [COLUMN_TYPE.TYPE]: true, + [COLUMN_TYPE.ENUM]: true, + [COLUMN_TYPE.DEFAULT]: true, + [COLUMN_TYPE.REQUIRED]: true, + [COLUMN_TYPE.DEPRECATED]: true, +}; + +function ruleOptionToColumnValues(ruleOption: RuleOption): { + [key in COLUMN_TYPE]: string | undefined; +} { + const columns: { + [key in COLUMN_TYPE]: string | undefined; + } = { + // Alphabetical order. + [COLUMN_TYPE.DEFAULT]: + ruleOption.default === undefined + ? undefined + : `\`${String(ruleOption.default)}\``, + [COLUMN_TYPE.DEPRECATED]: ruleOption.deprecated ? 'Yes' : undefined, + [COLUMN_TYPE.DESCRIPTION]: ruleOption.description, + [COLUMN_TYPE.ENUM]: + ruleOption.enum && ruleOption.enum.length > 0 + ? `\`${ruleOption.enum.join('`, `')}\`` + : undefined, + [COLUMN_TYPE.NAME]: `\`${ruleOption.name}\``, + [COLUMN_TYPE.REQUIRED]: ruleOption.required ? 'Yes' : undefined, + [COLUMN_TYPE.TYPE]: ruleOption.type + ? capitalizeOnlyFirstLetter(ruleOption.type) + : undefined, + }; + + return columns; +} + +function ruleOptionsToColumnsToDisplay(ruleOptions: readonly RuleOption[]): { + [key in COLUMN_TYPE]: boolean; +} { + const columnsToDisplay: { + [key in COLUMN_TYPE]: boolean; + } = { + // Alphabetical order. + [COLUMN_TYPE.DEFAULT]: ruleOptions.some((ruleOption) => ruleOption.default), + [COLUMN_TYPE.DEPRECATED]: ruleOptions.some( + (ruleOption) => ruleOption.deprecated + ), + [COLUMN_TYPE.DESCRIPTION]: ruleOptions.some( + (ruleOption) => ruleOption.description + ), + [COLUMN_TYPE.ENUM]: ruleOptions.some((ruleOption) => ruleOption.enum), + [COLUMN_TYPE.NAME]: true, + [COLUMN_TYPE.REQUIRED]: ruleOptions.some( + (ruleOption) => ruleOption.required + ), + [COLUMN_TYPE.TYPE]: ruleOptions.some((ruleOption) => ruleOption.type), + }; + return columnsToDisplay; +} + +function generateRuleOptionsListMarkdown(rule: RuleModule): string { + const ruleOptions = getAllNamedOptions(rule.meta.schema); + + if (ruleOptions.length === 0) { + return ''; + } + + const columnsToDisplay = ruleOptionsToColumnsToDisplay(ruleOptions); + const listHeaderRow = Object.keys(COLUMN_TYPE_DEFAULT_PRESENCE_AND_ORDERING) + .filter((type) => columnsToDisplay[type as COLUMN_TYPE]) + .map((type) => HEADERS[type as COLUMN_TYPE]); + + const rows = [...ruleOptions] + .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())) + .map((ruleOption) => { + const ruleOptionColumnValues = ruleOptionToColumnValues(ruleOption); + + // Recreate object using correct ordering and presence of columns. + return Object.keys(COLUMN_TYPE_DEFAULT_PRESENCE_AND_ORDERING) + .filter((type) => columnsToDisplay[type as COLUMN_TYPE]) + .map((type) => ruleOptionColumnValues[type as COLUMN_TYPE]); + }); + + return markdownTable( + [listHeaderRow, ...rows], + { align: 'l' } // Left-align headers. + ); +} + +export function updateRuleOptionsList( + markdown: string, + rule: RuleModule +): string { + const listStartIndex = markdown.indexOf(BEGIN_RULE_OPTIONS_LIST_MARKER); + let listEndIndex = markdown.indexOf(END_RULE_OPTIONS_LIST_MARKER); + + if (listStartIndex === -1 || listEndIndex === -1) { + // No rule options list found. + return markdown; + } + + // Account for length of pre-existing marker. + listEndIndex += END_RULE_OPTIONS_LIST_MARKER.length; + + const preList = markdown.slice(0, Math.max(0, listStartIndex)); + const postList = markdown.slice(Math.max(0, listEndIndex)); + + // New rule options list. + const list = generateRuleOptionsListMarkdown(rule); + + return `${preList}${BEGIN_RULE_OPTIONS_LIST_MARKER}\n\n${list}\n\n${END_RULE_OPTIONS_LIST_MARKER}${postList}`; +} diff --git a/lib/rule-options.ts b/lib/rule-options.ts index 551efbe3..52f95fea 100644 --- a/lib/rule-options.ts +++ b/lib/rule-options.ts @@ -1,14 +1,28 @@ import traverse from 'json-schema-traverse'; import type { JSONSchema } from '@typescript-eslint/utils'; +export type RuleOption = { + name: string; + type?: string; + description?: string; + required?: boolean; + enum?: readonly JSONSchema.JSONSchema4Type[]; + default?: JSONSchema.JSONSchema4Type; + deprecated?: boolean; +}; + /** * Gather a list of named options from a rule schema. * @param jsonSchema - the JSON schema to check * @returns - list of named options we could detect from the schema */ export function getAllNamedOptions( - jsonSchema: JSONSchema.JSONSchema4 | undefined | null -): readonly string[] { + jsonSchema: + | JSONSchema.JSONSchema4 + | readonly JSONSchema.JSONSchema4[] + | undefined + | null +): readonly RuleOption[] { if (!jsonSchema) { return []; } @@ -19,10 +33,23 @@ export function getAllNamedOptions( ); } - const options: string[] = []; + const options: RuleOption[] = []; traverse(jsonSchema, (js: JSONSchema.JSONSchema4) => { if (js.properties) { - options.push(...Object.keys(js.properties)); + options.push( + ...Object.entries(js.properties).map(([key, value]) => ({ + name: key, + type: value.type ? value.type.toString() : undefined, + description: value.description, + default: value.default, + enum: value.enum, + required: + typeof value.required === 'boolean' + ? value.required + : Array.isArray(js.required) && js.required.includes(key), + deprecated: value.deprecated, // eslint-disable-line @typescript-eslint/no-unsafe-assignment -- property exists on future JSONSchema version but we can let it be used anyway. + })) + ); } }); return options; @@ -33,7 +60,9 @@ export function getAllNamedOptions( * @param jsonSchema - the JSON schema to check * @returns - whether the schema has options */ -export function hasOptions(jsonSchema: JSONSchema.JSONSchema4): boolean { +export function hasOptions( + jsonSchema: JSONSchema.JSONSchema4 | readonly JSONSchema.JSONSchema4[] +): boolean { return ( (Array.isArray(jsonSchema) && jsonSchema.length > 0) || (typeof jsonSchema === 'object' && Object.keys(jsonSchema).length > 0) diff --git a/test/lib/generate/__snapshots__/rule-options-list-test.ts.snap b/test/lib/generate/__snapshots__/rule-options-list-test.ts.snap new file mode 100644 index 00000000..b242de0f --- /dev/null +++ b/test/lib/generate/__snapshots__/rule-options-list-test.ts.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generate (rule options list) basic generates the documentation 1`] = ` +"# test/no-foo + + +## Options + + +| Name | Description | Type | Choices | Default | Required | Deprecated | +| :---- | :---------------------------- | :------ | :---------------- | :------- | :------- | :--------- | +| \`bar\` | Choose how to use the rule. | String | \`always\`, \`never\` | \`always\` | Yes | | +| \`baz\` | | | | \`true\` | Yes | | +| \`biz\` | | | | | | | +| \`foo\` | Enable some kind of behavior. | Boolean | | \`false\` | | Yes | + +" +`; + +exports[`generate (rule options list) with no marker comments generates the documentation 1`] = ` +"# test/no-foo + + +## Options +foo" +`; + +exports[`generate (rule options list) with no options generates the documentation 1`] = ` +"# test/no-foo + + +## Options + + + + +" +`; diff --git a/test/lib/generate/rule-options-list-test.ts b/test/lib/generate/rule-options-list-test.ts new file mode 100644 index 00000000..3af6d07f --- /dev/null +++ b/test/lib/generate/rule-options-list-test.ts @@ -0,0 +1,166 @@ +import { generate } from '../../../lib/generator.js'; +import mockFs from 'mock-fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { readFileSync } from 'node:fs'; +import { jest } from '@jest/globals'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const PATH_NODE_MODULES = resolve(__dirname, '..', '..', '..', 'node_modules'); + +describe('generate (rule options list)', function () { + describe('basic', function () { + beforeEach(function () { + mockFs({ + 'package.json': JSON.stringify({ + name: 'eslint-plugin-test', + exports: 'index.js', + type: 'module', + }), + + 'index.js': ` + export default { + rules: { + 'no-foo': { + meta: { + schema: [{ + type: "object", + properties: { + foo: { + type: "boolean", + description: "Enable some kind of behavior.", + deprecated: true, + default: false + }, + bar: { + description: "Choose how to use the rule.", + type: "string", + enum: ["always", "never"], + default: "always" + }, + baz: { + default: true, + required: true, + }, + biz: {}, + }, + required: ["bar"], + additionalProperties: false + }], + }, + create(context) {} + }, + }, + configs: { + recommended: {}, + } + };`, + + 'README.md': '## Rules\n', + + 'docs/rules/no-foo.md': `## Options + +`, + + // Needed for some of the test infrastructure to work. + node_modules: mockFs.load(PATH_NODE_MODULES), + }); + }); + + afterEach(function () { + mockFs.restore(); + jest.resetModules(); + }); + + it('generates the documentation', async function () { + await generate('.'); + expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); + }); + }); + + describe('with no options', function () { + beforeEach(function () { + mockFs({ + 'package.json': JSON.stringify({ + name: 'eslint-plugin-test', + exports: 'index.js', + type: 'module', + }), + + 'index.js': ` + export default { + rules: { + 'no-foo': { + meta: {}, + create(context) {} + }, + }, + configs: { + recommended: {}, + } + };`, + + 'README.md': '## Rules\n', + + 'docs/rules/no-foo.md': `## Options + +`, + + // Needed for some of the test infrastructure to work. + node_modules: mockFs.load(PATH_NODE_MODULES), + }); + }); + + afterEach(function () { + mockFs.restore(); + jest.resetModules(); + }); + + it('generates the documentation', async function () { + await generate('.'); + expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); + }); + }); + + describe('with no marker comments', function () { + beforeEach(function () { + mockFs({ + 'package.json': JSON.stringify({ + name: 'eslint-plugin-test', + exports: 'index.js', + type: 'module', + }), + + 'index.js': ` + export default { + rules: { + 'no-foo': { + meta: { + schema: [{ type: "object", properties: { foo: { description: 'some desc' } } }] + }, + create(context) {} + }, + }, + };`, + + 'README.md': '## Rules\n', + + 'docs/rules/no-foo.md': '## Options\nfoo', + + // Needed for some of the test infrastructure to work. + node_modules: mockFs.load(PATH_NODE_MODULES), + }); + }); + + afterEach(function () { + mockFs.restore(); + jest.resetModules(); + }); + + it('generates the documentation', async function () { + await generate('.'); + expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); + }); + }); +}); diff --git a/test/lib/rule-options-test.ts b/test/lib/rule-options-test.ts index a8782e45..4fa8d8f4 100644 --- a/test/lib/rule-options-test.ts +++ b/test/lib/rule-options-test.ts @@ -3,23 +3,23 @@ import { getAllNamedOptions } from '../../lib/rule-options.js'; describe('rule options', function () { describe('#getAllNamedOptions', function () { it('handles null', function () { - expect(getAllNamedOptions(null)).toStrictEqual([]); // eslint-disable-line unicorn/no-null + expect(getAllNamedOptions(null)).toMatchInlineSnapshot('[]'); // eslint-disable-line unicorn/no-null }); it('handles undefined', function () { - expect(getAllNamedOptions(undefined)).toStrictEqual([]); // eslint-disable-line unicorn/no-useless-undefined + expect(getAllNamedOptions(undefined)).toMatchInlineSnapshot('[]'); // eslint-disable-line unicorn/no-useless-undefined }); it('handles empty array', function () { - expect(getAllNamedOptions([])).toStrictEqual([]); + expect(getAllNamedOptions([])).toMatchInlineSnapshot('[]'); }); it('handles array of empty object', function () { - expect(getAllNamedOptions([{}])).toStrictEqual([]); + expect(getAllNamedOptions([{}])).toMatchInlineSnapshot('[]'); }); it('handles empty object', function () { - expect(getAllNamedOptions({})).toStrictEqual([]); + expect(getAllNamedOptions({})).toMatchInlineSnapshot('[]'); }); it('handles object', function () { @@ -27,14 +27,56 @@ describe('rule options', function () { getAllNamedOptions({ type: 'object', properties: { - optionToDoSomething: { + optionToDoSomething1: { type: 'boolean', default: false, + deprecated: true, + }, + optionToDoSomething2: { + type: 'string', + enum: ['always', 'never'], + }, + optionToDoSomething3: { + required: true, }, }, + required: ['optionToDoSomething'], additionalProperties: false, }) - ).toStrictEqual(['optionToDoSomething']); + ).toMatchInlineSnapshot(` + [ + { + "default": false, + "deprecated": true, + "description": undefined, + "enum": undefined, + "name": "optionToDoSomething1", + "required": false, + "type": "boolean", + }, + { + "default": undefined, + "deprecated": undefined, + "description": undefined, + "enum": [ + "always", + "never", + ], + "name": "optionToDoSomething2", + "required": false, + "type": "string", + }, + { + "default": undefined, + "deprecated": undefined, + "description": undefined, + "enum": undefined, + "name": "optionToDoSomething3", + "required": true, + "type": undefined, + }, + ] + `); }); it('handles object in JS array', function () { @@ -51,7 +93,19 @@ describe('rule options', function () { additionalProperties: false, }, ]) - ).toStrictEqual(['optionToDoSomething']); + ).toMatchInlineSnapshot(` + [ + { + "default": false, + "deprecated": undefined, + "description": undefined, + "enum": undefined, + "name": "optionToDoSomething", + "required": false, + "type": "boolean", + }, + ] + `); }); it('handles multiple objects in JS array', function () { @@ -78,7 +132,28 @@ describe('rule options', function () { additionalProperties: false, }, ]) - ).toStrictEqual(['optionToDoSomething1', 'optionToDoSomething2']); + ).toMatchInlineSnapshot(` + [ + { + "default": false, + "deprecated": undefined, + "description": undefined, + "enum": undefined, + "name": "optionToDoSomething1", + "required": false, + "type": "boolean", + }, + { + "default": false, + "deprecated": undefined, + "description": undefined, + "enum": undefined, + "name": "optionToDoSomething2", + "required": false, + "type": "boolean", + }, + ] + `); }); it('handles object in array schema', function () { @@ -98,7 +173,19 @@ describe('rule options', function () { }, }, ]) - ).toStrictEqual(['optionToDoSomething']); + ).toMatchInlineSnapshot(` + [ + { + "default": false, + "deprecated": undefined, + "description": undefined, + "enum": undefined, + "name": "optionToDoSomething", + "required": false, + "type": "boolean", + }, + ] + `); }); it('handles array in object', function () { @@ -118,7 +205,19 @@ describe('rule options', function () { }, }, ]) - ).toStrictEqual(['optionToDoSomething']); + ).toMatchInlineSnapshot(` + [ + { + "default": false, + "deprecated": undefined, + "description": undefined, + "enum": undefined, + "name": "optionToDoSomething", + "required": false, + "type": "boolean", + }, + ] + `); }); it('handles object in array in object', function () { @@ -144,7 +243,28 @@ describe('rule options', function () { additionalProperties: false, }, ]) - ).toStrictEqual(['optionToDoSomething1', 'optionToDoSomething2']); + ).toMatchInlineSnapshot(` + [ + { + "default": undefined, + "deprecated": undefined, + "description": undefined, + "enum": undefined, + "name": "optionToDoSomething1", + "required": false, + "type": "array", + }, + { + "default": false, + "deprecated": undefined, + "description": undefined, + "enum": undefined, + "name": "optionToDoSomething2", + "required": false, + "type": "boolean", + }, + ] + `); }); }); });