From 917ce3cdd9bc27ceddc0987e8cdd8ae9062c3dcc Mon Sep 17 00:00:00 2001 From: Bryan Mishkin <698306+bmish@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:51:59 -0400 Subject: [PATCH] feat: add --init-emojis option to suggest emojis for configs --- README.md | 1 + lib/cli.ts | 8 + lib/generator.ts | 20 ++ lib/options.ts | 1 + lib/package-json.ts | 2 - lib/types.ts | 3 + package-lock.json | 265 ++++++++++++++++++- package.json | 2 + test/lib/__snapshots__/cli-test.ts.snap | 3 + test/lib/cli-test.ts | 3 + test/lib/generate/option-init-emojis-test.ts | 76 ++++++ 11 files changed, 374 insertions(+), 10 deletions(-) create mode 100644 test/lib/generate/option-init-emojis-test.ts diff --git a/README.md b/README.md index 3d7462c1..e3ec0c37 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ There's also a `postprocess` option that's only available via a [config file](#c | `--config-format` | The format to use for config names. See choices in below [table](#--config-format). | `name` | | `--ignore-config` | Config to ignore from being displayed. Often used for an `all` config. Option can be repeated. | | | `--ignore-deprecated-rules` | Whether to ignore deprecated rules from being checked, displayed, or updated. | `false` | +| `--init-emojis` | Whether to suggest an emoji for each config. Use `--config-emoji` option to utilize a suggestion. | `false` | | `--init-rule-docs` | Whether to create rule doc files if they don't yet exist. | `false` | | `--path-rule-doc` | Path to markdown file for each rule doc. Use `{name}` placeholder for the rule name. | `docs/rules/{name}.md` | | `--path-rule-list` | Path to markdown file where the rules table list should live. Option can be repeated. | `README.md` | diff --git a/lib/cli.ts b/lib/cli.ts index 86cc3d29..517d7b5e 100644 --- a/lib/cli.ts +++ b/lib/cli.ts @@ -95,6 +95,7 @@ async function loadConfigFileOptions(): Promise { configFormat: { type: 'string' }, ignoreConfig: schemaStringArray, ignoreDeprecatedRules: { type: 'boolean' }, + initEmojis: { type: 'boolean' }, initRuleDocs: { type: 'boolean' }, pathRuleDoc: { type: 'string' }, pathRuleList: { anyOf: [{ type: 'string' }, schemaStringArray] }, @@ -217,6 +218,13 @@ export async function run( )})`, parseBoolean ) + .option( + '--init-emojis [boolean]', + `(optional) Whether to suggest an emoji for each config. Use \`--config-emoji\` option to utilize a suggestion. (default: ${String( + OPTION_DEFAULTS[OPTION_TYPE.INIT_EMOJIS] + )})`, + parseBoolean + ) .option( '--init-rule-docs [boolean]', `(optional) Whether to create rule doc files if they don't yet exist. (default: ${String( diff --git a/lib/generator.ts b/lib/generator.ts index 513b6b1a..d955d003 100644 --- a/lib/generator.ts +++ b/lib/generator.ts @@ -26,6 +26,8 @@ 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 { getEmojis } from 'gpt-emoji'; +import { table } from 'table'; function stringOrArrayWithFallback( stringOrArray: undefined | T, @@ -75,6 +77,8 @@ export async function generate(path: string, options?: GenerateOptions) { const ignoreDeprecatedRules = options?.ignoreDeprecatedRules ?? OPTION_DEFAULTS[OPTION_TYPE.IGNORE_DEPRECATED_RULES]; + const initEmojis = + options?.initEmojis ?? OPTION_DEFAULTS[OPTION_TYPE.INIT_EMOJIS]; const initRuleDocs = options?.initRuleDocs ?? OPTION_DEFAULTS[OPTION_TYPE.INIT_RULE_DOCS]; const pathRuleDoc = @@ -113,6 +117,22 @@ export async function generate(path: string, options?: GenerateOptions) { const urlRuleDoc = options?.urlRuleDoc ?? OPTION_DEFAULTS[OPTION_TYPE.URL_RULE_DOC]; + if (initEmojis) { + if (!process.env.OPENAI_API_KEY) { + throw new Error('Missing OPENAI_API_KEY environment variable.'); + } + const configNames = Object.keys(plugin.configs || {}); + const result = await getEmojis(configNames, { + count: 1, + }); + const data = [ + ['Config', 'Emoji'], + ...configNames.map((configName, i) => [configName, result[i]]), + ]; + console.log(table(data)); + return; + } + // Gather normalized list of rules. const ruleNamesAndRules = Object.entries(plugin.rules) .map(([name, ruleModule]) => { diff --git a/lib/options.ts b/lib/options.ts index 07bd7fc5..0612ab99 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -49,6 +49,7 @@ export const OPTION_DEFAULTS = { [OPTION_TYPE.CONFIG_FORMAT]: DEFAULT_CONFIG_FORMAT, [OPTION_TYPE.IGNORE_CONFIG]: [], [OPTION_TYPE.IGNORE_DEPRECATED_RULES]: false, + [OPTION_TYPE.INIT_EMOJIS]: false, [OPTION_TYPE.INIT_RULE_DOCS]: false, [OPTION_TYPE.PATH_RULE_DOC]: join('docs', 'rules', '{name}.md'), [OPTION_TYPE.PATH_RULE_LIST]: ['README.md'], diff --git a/lib/package-json.ts b/lib/package-json.ts index a02c4ff2..4beb6c80 100644 --- a/lib/package-json.ts +++ b/lib/package-json.ts @@ -53,8 +53,6 @@ export async function loadPlugin(path: string): Promise { const propertiesToCheck: readonly (keyof PackageJson.ExportConditions)[] = ['.', 'node', 'import', 'require', 'default']; for (const prop of propertiesToCheck) { - // @ts-expect-error -- The union type for the object is causing trouble. - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const value = exports[prop]; if (typeof value === 'string') { pluginEntryPoint = value; diff --git a/lib/types.ts b/lib/types.ts index a9e28707..554ea1e4 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -100,6 +100,7 @@ export enum OPTION_TYPE { CONFIG_FORMAT = 'configFormat', IGNORE_CONFIG = 'ignoreConfig', IGNORE_DEPRECATED_RULES = 'ignoreDeprecatedRules', + INIT_EMOJIS = 'initEmojis', INIT_RULE_DOCS = 'initRuleDocs', PATH_RULE_DOC = 'pathRuleDoc', PATH_RULE_LIST = 'pathRuleList', @@ -164,6 +165,8 @@ export type GenerateOptions = { readonly ignoreConfig?: readonly string[]; /** Whether to ignore deprecated rules from being checked, displayed, or updated. Default: `false`. */ readonly ignoreDeprecatedRules?: boolean; + /** Whether to suggest an emoji for each config. Use `--config-emoji` option to utilize a suggestion. Default: `false`. */ + readonly initEmojis?: boolean; /** Whether to create rule doc files if they don't yet exist. Default: `false`. */ readonly initRuleDocs?: boolean; /** Path to markdown file for each rule doc. Use `{name}` placeholder for the rule name. Default: `docs/rules/{name}.md`. */ diff --git a/package-lock.json b/package-lock.json index f2814267..4a625ec7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,10 +16,12 @@ "cosmiconfig": "^8.0.0", "deepmerge": "^4.2.2", "dot-prop": "^7.2.0", + "gpt-emoji": "^0.2.1", "jest-diff": "^29.2.1", "json-schema-traverse": "^1.0.0", "markdown-table": "^3.0.3", "no-case": "^3.0.4", + "table": "^6.8.1", "type-fest": "^3.0.0" }, "bin": { @@ -2619,6 +2621,14 @@ "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "dev": true }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -2681,6 +2691,11 @@ "retry": "0.13.1" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -2702,6 +2717,14 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -4247,6 +4270,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", @@ -4630,6 +4664,14 @@ "node": ">= 6" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6217,6 +6259,25 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -6226,6 +6287,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/form-data-encoder": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.3.tgz", @@ -6707,6 +6781,17 @@ "url": "https://github.com/sindresorhus/got?sponsor=1" } }, + "node_modules/gpt-emoji": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/gpt-emoji/-/gpt-emoji-0.2.1.tgz", + "integrity": "sha512-CIDnHtgrymlPt+8WC1bohDsyCcomq/CSSRJ5MAFag90Sb6fpN2NJ0VdoozUGlwyCxpdW+qQNvzLLYsX/o8aPUQ==", + "dependencies": { + "openai": "^3.2.1" + }, + "engines": { + "node": "^14.18.0 || ^16.0.0 || >=18.0.0" + } + }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -7393,7 +7478,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -8822,6 +8906,11 @@ "lodash._reinterpolate": "^3.0.0" } }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==" + }, "node_modules/lodash.upperfirst": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", @@ -9709,7 +9798,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -9718,7 +9806,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -10534,6 +10621,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openai": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz", + "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", + "dependencies": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -12432,6 +12528,22 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -12906,6 +13018,39 @@ "node": "8.* || >= 10.*" } }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar": { "version": "6.1.12", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", @@ -15888,6 +16033,11 @@ "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + }, "async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -15949,6 +16099,11 @@ "retry": "0.13.1" } }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -15961,6 +16116,14 @@ "integrity": "sha512-b1WlTV8+XKLj9gZy2DZXgQiyDp9xkkoe2a6U6UbYccScq2wgH/YwCeI2/Jq2mgo0HzQxqJOjWZBLeA/mqsk5Mg==", "dev": true }, + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, "axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -17176,6 +17339,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", @@ -17468,6 +17639,11 @@ "vm2": "^3.9.8" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -18648,6 +18824,11 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -18657,6 +18838,16 @@ "is-callable": "^1.1.3" } }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "form-data-encoder": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.3.tgz", @@ -19021,6 +19212,14 @@ "responselike": "^3.0.0" } }, + "gpt-emoji": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/gpt-emoji/-/gpt-emoji-0.2.1.tgz", + "integrity": "sha512-CIDnHtgrymlPt+8WC1bohDsyCcomq/CSSRJ5MAFag90Sb6fpN2NJ0VdoozUGlwyCxpdW+qQNvzLLYsX/o8aPUQ==", + "requires": { + "openai": "^3.2.1" + } + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -19519,8 +19718,7 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-generator-fn": { "version": "2.1.0", @@ -20602,6 +20800,11 @@ "lodash._reinterpolate": "^3.0.0" } }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==" + }, "lodash.upperfirst": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", @@ -21178,14 +21381,12 @@ "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "requires": { "mime-db": "1.52.0" } @@ -21795,6 +21996,15 @@ "is-wsl": "^2.2.0" } }, + "openai": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz", + "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", + "requires": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -23188,6 +23398,16 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, "smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -23548,6 +23768,35 @@ "username-sync": "^1.0.2" } }, + "table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, "tar": { "version": "6.1.12", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", diff --git a/package.json b/package.json index 411762e6..7735d6b7 100644 --- a/package.json +++ b/package.json @@ -52,10 +52,12 @@ "cosmiconfig": "^8.0.0", "deepmerge": "^4.2.2", "dot-prop": "^7.2.0", + "gpt-emoji": "^0.2.1", "jest-diff": "^29.2.1", "json-schema-traverse": "^1.0.0", "markdown-table": "^3.0.3", "no-case": "^3.0.4", + "table": "^6.8.1", "type-fest": "^3.0.0" }, "devDependencies": { diff --git a/test/lib/__snapshots__/cli-test.ts.snap b/test/lib/__snapshots__/cli-test.ts.snap index b83931bf..0a249027 100644 --- a/test/lib/__snapshots__/cli-test.ts.snap +++ b/test/lib/__snapshots__/cli-test.ts.snap @@ -23,6 +23,7 @@ exports[`cli all CLI options and all config files options merges correctly, with "ignoredConfigFromCli2", ], "ignoreDeprecatedRules": true, + "initEmojis": false, "initRuleDocs": false, "pathRuleDoc": "www.example.com/rule-doc-from-cli", "pathRuleList": [ @@ -79,6 +80,7 @@ exports[`cli all CLI options, no config file options is called correctly 1`] = ` "ignoredConfigFromCli2", ], "ignoreDeprecatedRules": true, + "initEmojis": false, "initRuleDocs": false, "pathRuleDoc": "www.example.com/rule-doc-from-cli", "pathRuleList": [ @@ -126,6 +128,7 @@ exports[`cli all config files options, no CLI options is called correctly 1`] = "ignoredConfigFromConfigFile2", ], "ignoreDeprecatedRules": true, + "initEmojis": true, "initRuleDocs": true, "pathRuleDoc": "www.example.com/rule-doc-from-config-file", "pathRuleList": [ diff --git a/test/lib/cli-test.ts b/test/lib/cli-test.ts index 38690047..cd0a4a22 100644 --- a/test/lib/cli-test.ts +++ b/test/lib/cli-test.ts @@ -12,6 +12,7 @@ const configFileOptionsAll: { [key in OPTION_TYPE]: unknown } = { 'ignoredConfigFromConfigFile2', ], ignoreDeprecatedRules: true, + initEmojis: true, initRuleDocs: true, pathRuleDoc: 'www.example.com/rule-doc-from-config-file', pathRuleList: 'www.example.com/rule-list-from-config-file', @@ -49,6 +50,8 @@ const cliOptionsAll: { [key in OPTION_TYPE]: readonly string[] } = { [OPTION_TYPE.IGNORE_DEPRECATED_RULES]: ['--ignore-deprecated-rules', 'true'], + [OPTION_TYPE.INIT_EMOJIS]: ['--init-emojis', 'false'], + [OPTION_TYPE.INIT_RULE_DOCS]: ['--init-rule-docs', 'false'], [OPTION_TYPE.PATH_RULE_DOC]: [ diff --git a/test/lib/generate/option-init-emojis-test.ts b/test/lib/generate/option-init-emojis-test.ts new file mode 100644 index 00000000..6ee0ee18 --- /dev/null +++ b/test/lib/generate/option-init-emojis-test.ts @@ -0,0 +1,76 @@ +import { generate } from '../../../lib/generator.js'; +import mockFs from 'mock-fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { jest } from '@jest/globals'; +import * as sinon from 'sinon'; +import * as gptEmoji from 'gpt-emoji'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const PATH_NODE_MODULES = resolve(__dirname, '..', '..', '..', 'node_modules'); + +describe('generate (initEmojis option)', 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: {}, + create(context) {} + }, + }, + configs: { + recommended: {}, + errors: {}, + } + };`, + + 'README.md': '## Rules\n', + + '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 without env var', async function () { + await expect(generate('.', { initEmojis: true })).rejects.toThrow( + 'Missing OPENAI_API_KEY environment variable.' + ); + }); + + it('prints a table', async function () { + const consoleLogStub = sinon.stub(console, 'log'); + const getEmojisStub = sinon + .stub(gptEmoji, 'getEmojis') + .resolves([['👍'], ['❗️']]); + + await generate('.', { + initEmojis: true, + }); + + expect(getEmojisStub.callCount).toBe(1); + expect(getEmojisStub.firstCall.args).toStrictEqual([]); // TODO + + expect(consoleLogStub.callCount).toBe(1); + expect(consoleLogStub.firstCall.args).toStrictEqual([]); // TODO + + consoleLogStub.restore(); + }); + }); +});