From 9a14b9e07c21462b85d35890d0a12712b8af31be Mon Sep 17 00:00:00 2001 From: Bryan Mishkin <698306+bmish@users.noreply.github.com> Date: Fri, 16 Dec 2022 20:08:24 -0500 Subject: [PATCH] chore: better normalize pathRuleList option --- lib/generator.ts | 26 ++++++++++--- lib/options.ts | 2 +- test/lib/generate/file-paths-test.ts | 57 ++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/lib/generator.ts b/lib/generator.ts index 7fea7fbb..4202eb66 100644 --- a/lib/generator.ts +++ b/lib/generator.ts @@ -91,6 +91,25 @@ function stringOrArrayWithFallback( return stringOrArray && stringOrArray.length > 0 ? stringOrArray : fallback; } +function stringOrArrayToArrayWithFallback( + stringOrArray: undefined | string | readonly string[], + fallback: readonly string[] +): readonly string[] { + const asArray = + stringOrArray instanceof Array // eslint-disable-line unicorn/no-instanceof-array -- using Array.isArray() loses type information about the array. + ? stringOrArray + : stringOrArray + ? [stringOrArray] + : []; + const csvStringItem = asArray.find((item) => item.includes(',')); + if (csvStringItem) { + throw new Error( + `Provide property as array, not a CSV string: ${csvStringItem}` + ); + } + return asArray && asArray.length > 0 ? asArray : fallback; +} + // eslint-disable-next-line complexity export async function generate(path: string, options?: GenerateOptions) { const plugin = await loadPlugin(path); @@ -115,7 +134,7 @@ export async function generate(path: string, options?: GenerateOptions) { options?.initRuleDocs ?? OPTION_DEFAULTS[OPTION_TYPE.INIT_RULE_DOCS]; const pathRuleDoc = options?.pathRuleDoc ?? OPTION_DEFAULTS[OPTION_TYPE.PATH_RULE_DOC]; - const pathRuleList = stringOrArrayWithFallback( + const pathRuleList = stringOrArrayToArrayWithFallback( options?.pathRuleList, OPTION_DEFAULTS[OPTION_TYPE.PATH_RULE_LIST] ); @@ -269,10 +288,7 @@ export async function generate(path: string, options?: GenerateOptions) { ); } - // eslint-disable-next-line unicorn/no-instanceof-array -- using Array.isArray() loses type information about the array. - for (const pathRuleListItem of pathRuleList instanceof Array - ? pathRuleList - : [pathRuleList]) { + for (const pathRuleListItem of pathRuleList) { // Find the exact filename. const pathToFile = getPathWithExactFileNameCasing( join(path, pathRuleListItem) diff --git a/lib/options.ts b/lib/options.ts index 4df4c05f..a27c101b 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -47,7 +47,7 @@ export const OPTION_DEFAULTS = { [OPTION_TYPE.IGNORE_DEPRECATED_RULES]: false, [OPTION_TYPE.INIT_RULE_DOCS]: false, [OPTION_TYPE.PATH_RULE_DOC]: join('docs', 'rules', '{name}.md'), - [OPTION_TYPE.PATH_RULE_LIST]: 'README.md', + [OPTION_TYPE.PATH_RULE_LIST]: ['README.md'], [OPTION_TYPE.POSTPROCESS]: (content: string) => content, [OPTION_TYPE.RULE_DOC_NOTICES]: Object.entries( NOTICE_TYPE_DEFAULT_PRESENCE_AND_ORDERING diff --git a/test/lib/generate/file-paths-test.ts b/test/lib/generate/file-paths-test.ts index b6f42fe5..90a56f3c 100644 --- a/test/lib/generate/file-paths-test.ts +++ b/test/lib/generate/file-paths-test.ts @@ -262,6 +262,63 @@ describe('generate (file paths)', function () { }); }); + describe('multiple rules lists but incorrectly using CSV string for option', 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) {} }, + }, + };`, + + 'README.md': + '', + 'rules/list.md': + '', + 'docs/rules/no-foo.md': '', + + // Needed for some of the test infrastructure to work. + node_modules: mockFs.load(PATH_NODE_MODULES), + }); + }); + + afterEach(function () { + mockFs.restore(); + jest.resetModules(); + }); + + it('throws an error', async function () { + await expect( + generate('.', { + pathRuleList: `README.md,${join('rules', 'list.md')}`, + }) + ).rejects.toThrow( + `Provide property as array, not a CSV string: README.md,${join( + 'rules', + 'list.md' + )}` + ); + + await expect( + generate('.', { + pathRuleList: [`README.md,${join('rules', 'list.md')}`], + }) + ).rejects.toThrow( + `Provide property as array, not a CSV string: README.md,${join( + 'rules', + 'list.md' + )}` + ); + }); + }); + describe('empty array of rule lists (happens when CLI option is not passed)', function () { beforeEach(function () { mockFs({