diff --git a/src/next/base.ts b/src/next/base.ts
index cbab6de6..11e592f6 100644
--- a/src/next/base.ts
+++ b/src/next/base.ts
@@ -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()
diff --git a/src/next/blocks/blockREADME.test.ts b/src/next/blocks/blockREADME.test.ts
index 548215b1..452d47ca 100644
--- a/src/next/blocks/blockREADME.test.ts
+++ b/src/next/blocks/blockREADME.test.ts
@@ -62,7 +62,7 @@ describe("blockREADME", () => {
"README.md": "
Test Title
- First sentence.
+ First sentence.
Second sentence.
@@ -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": "Test Title
+
+ Test description
+
+
+
+
+
+
+
+
+
+ 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: {
@@ -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": "Test Title
+
+ Test description
+
+
+
+
+
+
+
+
+
+
+
+ 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,
diff --git a/src/next/blocks/blockREADME.ts b/src/next/blocks/blockREADME.ts
index 84873635..70f3c9cf 100644
--- a/src/next/blocks/blockREADME.ts
+++ b/src/next/blocks/blockREADME.ts
@@ -5,6 +5,7 @@ import { base } from "../base.js";
function printAttributes(attributes: Record) {
return Object.entries(attributes)
.map(([key, value]) => `${key}="${value}"`)
+ .sort()
.join(" ");
}
@@ -20,9 +21,12 @@ export const blockREADME = base.createBlock({
produce({ addons, options }) {
const { badges, notices, sections } = addons;
- const logo = options.logo
- ? `\n\n`
- : "";
+ const logo =
+ options.logo &&
+ `\n\n`;
+
+ const explainer =
+ options.explainer && `\n${options.explainer.join("\n")}\n`;
return {
files: {
@@ -37,7 +41,7 @@ export const blockREADME = base.createBlock({
-${logo}
+${[logo, explainer].filter(Boolean).join("")}
## Usage
${options.usage}
@@ -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";
}
diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts
index c9559ccc..82098f8e 100644
--- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts
+++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts
@@ -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(`
+
+
+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(`
+
+
+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(
@@ -107,7 +151,7 @@ describe("readDefaultsFromReadme", () => {
const logo = await readDefaultsFromReadme(
() =>
Promise.resolve(`
-`),
+`),
() => Promise.resolve(undefined),
).logo();
diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts
index 41fb6ed8..9b8aaf38 100644
--- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts
+++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts
@@ -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();
@@ -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>/.exec(text) ??