Skip to content

Commit 76569f7

Browse files
feat: added configEmoji Addon to blockESLintPlugin
1 parent 7a5c680 commit 76569f7

File tree

6 files changed

+293
-24
lines changed

6 files changed

+293
-24
lines changed

src/blocks/blockESLintPlugin.test.ts

Lines changed: 229 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { testBlock } from "bingo-stratum-testers";
2-
import { describe, expect, test } from "vitest";
1+
import { testBlock, testIntake } from "bingo-stratum-testers";
2+
import { describe, expect, it, test } from "vitest";
33

44
import { blockESLintPlugin } from "./blockESLintPlugin.js";
55
import { optionsBase } from "./options.fakes.js";
66

77
describe("blockESLintPlugin", () => {
8-
test("without options or mode", () => {
8+
test("without addons, mode, or options", () => {
99
const creation = testBlock(blockESLintPlugin, {
1010
options: optionsBase,
1111
});
@@ -506,4 +506,230 @@ describe("blockESLintPlugin", () => {
506506
}
507507
`);
508508
});
509+
510+
test("addons", () => {
511+
const creation = testBlock(blockESLintPlugin, {
512+
addons: {
513+
configEmoji: [
514+
["recommended", "✅"],
515+
["legacy-recommended", "✔️"],
516+
],
517+
},
518+
options: optionsBase,
519+
});
520+
521+
expect(creation).toMatchInlineSnapshot(`
522+
{
523+
"addons": [
524+
{
525+
"addons": {
526+
"words": [
527+
"eslint-doc-generatorrc",
528+
],
529+
},
530+
"block": [Function],
531+
},
532+
{
533+
"addons": {
534+
"sections": {
535+
"Building": {
536+
"innerSections": [
537+
{
538+
"contents": "
539+
Run [\`eslint-doc-generator\`](https://github.com/bmish/eslint-doc-generator) to generate Markdown files documenting rules.
540+
541+
\`\`\`shell
542+
pnpm build:docs
543+
\`\`\`
544+
",
545+
"heading": "Building Docs",
546+
},
547+
],
548+
},
549+
"Linting": {
550+
"contents": {
551+
"items": [
552+
"- \`pnpm lint:docs\` ([eslint-doc-generator](https://github.com/bmish/eslint-doc-generator)): Generates and validates documentation for ESLint rules",
553+
],
554+
},
555+
},
556+
},
557+
},
558+
"block": [Function],
559+
},
560+
{
561+
"addons": {
562+
"extensions": [
563+
"eslintPlugin.configs["flat/recommended"]",
564+
],
565+
"ignores": [
566+
".eslint-doc-generatorrc.js",
567+
"docs/rules/*/*.ts",
568+
],
569+
"imports": [
570+
{
571+
"source": {
572+
"packageName": "eslint-plugin-eslint-plugin",
573+
"version": "6.4.0",
574+
},
575+
"specifier": "eslintPlugin",
576+
},
577+
],
578+
},
579+
"block": [Function],
580+
},
581+
{
582+
"addons": {
583+
"jobs": [
584+
{
585+
"name": "Lint Docs",
586+
"steps": [
587+
{
588+
"run": "pnpm build || exit 0",
589+
},
590+
{
591+
"run": "pnpm lint:docs",
592+
},
593+
],
594+
},
595+
],
596+
},
597+
"block": [Function],
598+
},
599+
{
600+
"addons": {
601+
"properties": {
602+
"devDependencies": {
603+
"eslint-doc-generator": "2.1.0",
604+
"eslint-plugin-eslint-plugin": "6.4.0",
605+
},
606+
"scripts": {
607+
"build:docs": "pnpm build --no-dts && eslint-doc-generator",
608+
"lint:docs": "eslint-doc-generator --check",
609+
},
610+
},
611+
},
612+
"block": [Function],
613+
},
614+
{
615+
"addons": {
616+
"coverage": {
617+
"exclude": [
618+
"src/index.ts",
619+
"src/rules/index.ts",
620+
],
621+
},
622+
},
623+
"block": [Function],
624+
},
625+
],
626+
"files": {
627+
".eslint-doc-generatorrc.js": "import prettier from "prettier";
628+
629+
/** @type {import('eslint-doc-generator').GenerateOptions} */
630+
const config = {
631+
configEmoji: [["recommended","✅"],["legacy-recommended","✔️"]],
632+
postprocess: async (content, path) =>
633+
prettier.format(content, {
634+
...(await prettier.resolveConfig(path)),
635+
parser: "markdown",
636+
}),
637+
ruleDocTitleFormat: "name",
638+
};
639+
640+
export default config;
641+
",
642+
},
643+
}
644+
`);
645+
});
646+
647+
describe("intake", () => {
648+
it("returns nothing when .eslint-doc-generatorrc.js and .eslint-doc-generatorrc.mjs do not exist", () => {
649+
const actual = testIntake(blockESLintPlugin, {
650+
files: {},
651+
options: optionsBase,
652+
});
653+
654+
expect(actual).toEqual(undefined);
655+
});
656+
657+
it("returns nothing when .eslint-doc-generatorrc.js passes nothing to config =", () => {
658+
const actual = testIntake(blockESLintPlugin, {
659+
files: {
660+
".eslint-doc-generatorrc.js": [`config = {};`],
661+
},
662+
options: optionsBase,
663+
});
664+
665+
expect(actual).toEqual(undefined);
666+
});
667+
668+
it("returns nothing when .eslint-doc-generatorrc.js passes invalid syntax to config =", () => {
669+
const actual = testIntake(blockESLintPlugin, {
670+
files: {
671+
".eslint-doc-generatorrc.js": [`config = { ! }`],
672+
},
673+
options: optionsBase,
674+
});
675+
676+
expect(actual).toEqual(undefined);
677+
});
678+
679+
it("returns nothing when .eslint-doc-generatorrc.js passes unrelated properties to config =", () => {
680+
const actual = testIntake(blockESLintPlugin, {
681+
files: {
682+
".eslint-doc-generatorrc.js": [`config = { other: true }`],
683+
},
684+
options: optionsBase,
685+
});
686+
687+
expect(actual).toEqual({});
688+
});
689+
690+
it("returns configEmoji when it exists alone in .eslint-doc-generatorrc.js", () => {
691+
const actual = testIntake(blockESLintPlugin, {
692+
files: {
693+
".eslint-doc-generatorrc.js": [
694+
`config = { configEmoji: [["recommended", "✅"]] }`,
695+
],
696+
},
697+
options: optionsBase,
698+
});
699+
700+
expect(actual).toEqual({
701+
configEmoji: [["recommended", "✅"]],
702+
});
703+
});
704+
});
705+
706+
it("returns configEmoji when it exists alone in .eslint-doc-generatorrc.mjs", () => {
707+
const actual = testIntake(blockESLintPlugin, {
708+
files: {
709+
".eslint-doc-generatorrc.mjs": [
710+
`config = { configEmoji: [["recommended", "✅"]] }`,
711+
],
712+
},
713+
options: optionsBase,
714+
});
715+
716+
expect(actual).toEqual({
717+
configEmoji: [["recommended", "✅"]],
718+
});
719+
});
720+
721+
it("returns configEmoji when it exists with other data in .eslint-doc-generatorrc.js", () => {
722+
const actual = testIntake(blockESLintPlugin, {
723+
files: {
724+
".eslint-doc-generatorrc.js": [
725+
`config = { configEmoji: [["recommended", "✅"]], other: true }`,
726+
],
727+
},
728+
options: optionsBase,
729+
});
730+
731+
expect(actual).toEqual({
732+
configEmoji: [["recommended", "✅"]],
733+
});
734+
});
509735
});

src/blocks/blockESLintPlugin.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,40 @@
1+
import { z } from "zod";
2+
13
import { base } from "../base.js";
24
import { blockCSpell } from "./blockCSpell.js";
35
import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js";
46
import { blockESLint } from "./blockESLint.js";
57
import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js";
68
import { blockPackageJson } from "./blockPackageJson.js";
79
import { blockVitest } from "./blockVitest.js";
10+
import { intakeFileWithConfig } from "./intake/intakeFileWithConfig.js";
11+
12+
const zConfigEmoji = z.array(z.tuple([z.string(), z.string()])).optional();
813

914
export const blockESLintPlugin = base.createBlock({
1015
about: {
1116
name: "ESLint Plugin",
1217
},
13-
produce({ options }) {
18+
addons: {
19+
configEmoji: zConfigEmoji,
20+
},
21+
intake({ files }) {
22+
const configRaw = intakeFileWithConfig(
23+
files,
24+
[[".eslint-doc-generatorrc.js", ".eslint-doc-generatorrc.mjs"]],
25+
/config\s*=\s*\{(.+)\}$/u,
26+
);
27+
28+
if (!configRaw) {
29+
return undefined;
30+
}
31+
32+
return {
33+
configEmoji: zConfigEmoji.safeParse(configRaw.configEmoji).data,
34+
};
35+
},
36+
produce({ addons, options }) {
37+
const { configEmoji } = addons;
1438
const configFileName = `.eslint-doc-generatorrc.${options.type === "commonjs" ? "mjs" : "js"}`;
1539

1640
return {
@@ -90,7 +114,7 @@ pnpm build:docs
90114
91115
/** @type {import('eslint-doc-generator').GenerateOptions} */
92116
const config = {
93-
postprocess: async (content, path) =>
117+
${configEmoji ? `configEmoji: ${JSON.stringify(configEmoji)},\n\t` : ""}postprocess: async (content, path) =>
94118
prettier.format(content, {
95119
...(await prettier.resolveConfig(path)),
96120
parser: "markdown",

src/blocks/blockTSup.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { blockReleaseIt } from "./blockReleaseIt.js";
1111
import { blockRemoveDependencies } from "./blockRemoveDependencies.js";
1212
import { blockRemoveFiles } from "./blockRemoveFiles.js";
1313
import { blockRemoveWorkflows } from "./blockRemoveWorkflows.js";
14-
import { intakeFileDefineConfig } from "./intake/intakeFileDefineConfig.js";
14+
import { intakeFileWithConfig } from "./intake/intakeFileWithConfig.js";
1515
import { CommandPhase } from "./phases.js";
1616

1717
const zEntry = z.array(z.string());
@@ -27,12 +27,16 @@ export const blockTSup = base.createBlock({
2727
runInCI: z.array(z.string()).default([]),
2828
},
2929
intake({ files }) {
30-
const rawData = intakeFileDefineConfig(files, ["tsup.config.ts"]);
31-
if (!rawData) {
30+
const configRaw = intakeFileWithConfig(
31+
files,
32+
["tsup.config.ts"],
33+
/defineConfig\(\{(.+)\}\)\s*(?:;\s*)?$/u,
34+
);
35+
if (!configRaw) {
3236
return undefined;
3337
}
3438

35-
const { entry: rawEntry, ...rest } = rawData;
39+
const { entry: rawEntry, ...rest } = configRaw;
3640

3741
return {
3842
entry: zEntry.safeParse(rawEntry).data,

src/blocks/blockVitest.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { blockRemoveFiles } from "./blockRemoveFiles.js";
1717
import { blockRemoveWorkflows } from "./blockRemoveWorkflows.js";
1818
import { blockTSup } from "./blockTSup.js";
1919
import { blockVSCode } from "./blockVSCode.js";
20-
import { intakeFileDefineConfig } from "./intake/intakeFileDefineConfig.js";
20+
import { intakeFileWithConfig } from "./intake/intakeFileWithConfig.js";
2121

2222
const zCoverage = z.object({
2323
exclude: z.array(z.string()).optional(),
@@ -37,7 +37,11 @@ const zTest = z
3737
.partial();
3838

3939
function intakeFromConfig(files: IntakeDirectory) {
40-
const rawData = intakeFileDefineConfig(files, ["vitest.config.ts"]);
40+
const rawData = intakeFileWithConfig(
41+
files,
42+
["vitest.config.ts"],
43+
/defineConfig\(\{(.+)\}\)\s*(?:;\s*)?$/u,
44+
);
4145
if (typeof rawData?.test !== "object") {
4246
return undefined;
4347
}

0 commit comments

Comments
 (0)