Skip to content

Commit

Permalink
feat: add options.explainer for blockREADME (#1879)
Browse files Browse the repository at this point in the history
## PR Checklist

- [x] Addresses an existing open issue: fixes #1878
- [x] That issue was marked as [`status: accepting
prs`](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22)
- [x] Steps in
[CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/create-typescript-app/blob/main/.github/CONTRIBUTING.md)
were taken

## Overview

Also touches up some spacing and tests in `readDefaultsFromReadme`,
while I'm in the area.

💖
  • Loading branch information
JoshuaKGoldberg authored Jan 15, 2025
1 parent 1fac1a1 commit 1249f4d
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 9 deletions.
4 changes: 4 additions & 0 deletions src/next/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ export const base = createBase({
.describe(
"email address to be listed as the point of contact in docs and packages",
),
explainer: z
.array(z.string())
.optional()
.describe("additional README.md sentence(s) describing the package"),
funding: z
.string()
.optional()
Expand Down
92 changes: 91 additions & 1 deletion src/next/blocks/blockREADME.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe("blockREADME", () => {
"README.md": "<h1 align="center">Test Title</h1>
<p align="center">
First sentence.
First sentence.
Second sentence.
</p>
Expand All @@ -89,6 +89,47 @@ describe("blockREADME", () => {
`);
});

test("options.explainer", () => {
const creation = testBlock(blockREADME, {
options: {
...options,
explainer: ["And a one.", "And a two."],
},
});

expect(creation).toMatchInlineSnapshot(`
{
"files": {
"README.md": "<h1 align="center">Test Title</h1>
<p align="center">Test description</p>
<p align="center">
<a href="https://github.com/test-owner/test-repository/blob/main/.github/CODE_OF_CONDUCT.md" target="_blank"><img alt="🤝 Code of Conduct: Kept" src="https://img.shields.io/badge/%F0%9F%A4%9D_code_of_conduct-kept-21bb42" /></a>
<a href="https://codecov.io/gh/test-owner/test-repository" target="_blank"><img alt="🧪 Coverage" src="https://img.shields.io/codecov/c/github/test-owner/test-repository?label=%F0%9F%A7%AA%20coverage" /></a>
<a href="https://github.com/test-owner/test-repository/blob/main/LICENSE.md" target="_blank"><img alt="📝 License: MIT" src="https://img.shields.io/badge/%F0%9F%93%9D_license-MIT-21bb42.svg"></a>
<a href="http://npmjs.com/package/test-repository"><img alt="📦 npm version" src="https://img.shields.io/npm/v/test-repository?color=21bb42&label=%F0%9F%93%A6%20npm" /></a>
<img alt="💪 TypeScript: Strict" src="https://img.shields.io/badge/%F0%9F%92%AA_typescript-strict-21bb42.svg" />
</p>
And a one.
And a two.
## Usage
Use it.
## Development
See [\`.github/CONTRIBUTING.md\`](./.github/CONTRIBUTING.md), then [\`.github/DEVELOPMENT.md\`](./.github/DEVELOPMENT.md).
Thanks! 💖
",
},
}
`);
});

test("options.logo without sizing", () => {
const creation = testBlock(blockREADME, {
options: {
Expand Down Expand Up @@ -177,6 +218,55 @@ describe("blockREADME", () => {
`);
});

test("options.explainer and options.logo", () => {
const creation = testBlock(blockREADME, {
options: {
...options,
explainer: ["And a one.", "And a two."],
logo: {
alt: "My logo",
height: 100,
src: "img.jpg",
width: 128,
},
},
});

expect(creation).toMatchInlineSnapshot(`
{
"files": {
"README.md": "<h1 align="center">Test Title</h1>
<p align="center">Test description</p>
<p align="center">
<a href="https://github.com/test-owner/test-repository/blob/main/.github/CODE_OF_CONDUCT.md" target="_blank"><img alt="🤝 Code of Conduct: Kept" src="https://img.shields.io/badge/%F0%9F%A4%9D_code_of_conduct-kept-21bb42" /></a>
<a href="https://codecov.io/gh/test-owner/test-repository" target="_blank"><img alt="🧪 Coverage" src="https://img.shields.io/codecov/c/github/test-owner/test-repository?label=%F0%9F%A7%AA%20coverage" /></a>
<a href="https://github.com/test-owner/test-repository/blob/main/LICENSE.md" target="_blank"><img alt="📝 License: MIT" src="https://img.shields.io/badge/%F0%9F%93%9D_license-MIT-21bb42.svg"></a>
<a href="http://npmjs.com/package/test-repository"><img alt="📦 npm version" src="https://img.shields.io/npm/v/test-repository?color=21bb42&label=%F0%9F%93%A6%20npm" /></a>
<img alt="💪 TypeScript: Strict" src="https://img.shields.io/badge/%F0%9F%92%AA_typescript-strict-21bb42.svg" />
</p>
<img align="right" alt="My logo" height="100" src="img.jpg" width="128">
And a one.
And a two.
## Usage
Use it.
## Development
See [\`.github/CONTRIBUTING.md\`](./.github/CONTRIBUTING.md), then [\`.github/DEVELOPMENT.md\`](./.github/DEVELOPMENT.md).
Thanks! 💖
",
},
}
`);
});

test("without addons", () => {
const creation = testBlock(blockREADME, {
options,
Expand Down
14 changes: 9 additions & 5 deletions src/next/blocks/blockREADME.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { base } from "../base.js";
function printAttributes(attributes: Record<string, number | string>) {
return Object.entries(attributes)
.map(([key, value]) => `${key}="${value}"`)
.sort()
.join(" ");
}

Expand All @@ -20,9 +21,12 @@ export const blockREADME = base.createBlock({
produce({ addons, options }) {
const { badges, notices, sections } = addons;

const logo = options.logo
? `\n<img ${printAttributes({ align: "right", ...options.logo })}>\n`
: "";
const logo =
options.logo &&
`\n<img ${printAttributes({ align: "right", ...options.logo })}>\n`;

const explainer =
options.explainer && `\n${options.explainer.join("\n")}\n`;

return {
files: {
Expand All @@ -37,7 +41,7 @@ export const blockREADME = base.createBlock({
<a href="http://npmjs.com/package/${options.repository}"><img alt="📦 npm version" src="https://img.shields.io/npm/v/${options.repository}?color=21bb42&label=%F0%9F%93%A6%20npm" /></a>
<img alt="💪 TypeScript: Strict" src="https://img.shields.io/badge/%F0%9F%92%AA_typescript-strict-21bb42.svg" />
</p>
${logo}
${[logo, explainer].filter(Boolean).join("")}
## Usage
${options.usage}
Expand All @@ -58,5 +62,5 @@ function formatDescription(description: string) {
return description;
}

return "\n\t" + description.replaceAll(". ", ". \n\t") + "\n";
return "\n\t" + description.replaceAll(". ", ".\n\t") + "\n";
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,50 @@ vi.mock("./getUsageFromReadme.js", () => ({
}));

describe("readDefaultsFromReadme", () => {
describe("explainer", () => {
it("defaults to undefined when it cannot be found", async () => {
const explainer = await readDefaultsFromReadme(
() => Promise.resolve(`nothing.`),
() => Promise.resolve(undefined),
).explainer();

expect(explainer).toBeUndefined();
});

it("parses a line after badges", async () => {
const explainer = await readDefaultsFromReadme(
() =>
Promise.resolve(`
</p>
This is my project.
## Usage
.`),
() => Promise.resolve(undefined),
).explainer();

expect(explainer).toEqual(["This is my project."]);
});

it("parses multiple line after badges", async () => {
const explainer = await readDefaultsFromReadme(
() =>
Promise.resolve(`
</p>
This is my project.
It is good.
## Usage
.`),
() => Promise.resolve(undefined),
).explainer();

expect(explainer).toEqual(["This is my project.", "It is good."]);
});
});

describe("logo", () => {
it("defaults to undefined when it cannot be found", async () => {
const logo = await readDefaultsFromReadme(
Expand Down Expand Up @@ -107,7 +151,7 @@ describe("readDefaultsFromReadme", () => {
const logo = await readDefaultsFromReadme(
() =>
Promise.resolve(`
<img alt='Project logo: a fancy circle' src='abc/def.jpg'/>`),
<img alt='Project logo: a fancy circle' height='117px' src='abc/def.jpg' width=' 117px'/>`),
() => Promise.resolve(undefined),
).logo();

Expand Down
16 changes: 14 additions & 2 deletions src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ export function readDefaultsFromReadme(
);

return {
explainer: async () => {
return />\n\n([\s\S]*?)\n\n## Usage/u
.exec(await readme())?.[1]
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
},

logo: async () => {
const tag = await imageTag();

Expand All @@ -22,18 +30,22 @@ export function readDefaultsFromReadme(

const src = /src\s*=(.+)['"/]>/
.exec(tag)?.[1]
?.replaceAll(/^['"]|['"]$/g, "");
?.split(/\s*\w+=/)[0]
.replaceAll(/^['"]|['"]$/g, "");

if (!src) {
return undefined;
}

return {
alt: /alt=['"](.+)['"]\s*src=/.exec(tag)?.[1] ?? "Project logo",
alt:
/alt=['"](.+)['"]\s*src=/.exec(tag)?.[1].split(/['"]?\s*\w+=/)[0] ??
"Project logo",
src,
...readLogoSizing(src),
};
},

title: async () => {
const text = await readme();
const fromText = (/^<h1\s+align="center">(.+)<\/h1>/.exec(text) ??
Expand Down

0 comments on commit 1249f4d

Please sign in to comment.