From 95df083a92960d53e2485af74f52a32cb8e21f5b Mon Sep 17 00:00:00 2001 From: Josh Goldberg <git@joshuakgoldberg.com> Date: Fri, 4 Apr 2025 08:50:21 -0400 Subject: [PATCH] fix: correct README.md parsing without an existing Usage h2 --- src/options/readReadmeExplainer.test.ts | 70 ++++++++++++++++++++++--- src/options/readReadmeExplainer.ts | 26 ++++----- 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/options/readReadmeExplainer.test.ts b/src/options/readReadmeExplainer.test.ts index d545d13c3..0beed9327 100644 --- a/src/options/readReadmeExplainer.test.ts +++ b/src/options/readReadmeExplainer.test.ts @@ -37,7 +37,8 @@ describe(readReadmeExplainer, () => { This is my project. ## Usage - .`), + +...`), ); expect(actual).toEqual("This is my project."); @@ -52,7 +53,8 @@ This is my project. It is good. ## Usage - .`), + +...`), ); expect(actual).toEqual("This is my project.\nIt is good."); @@ -80,7 +82,8 @@ This is my project. It is good. ## Usage - .`), + +...`), ); expect(actual).toEqual("This is my project.\nIt is good."); @@ -108,7 +111,8 @@ This is my project. It is good. ## Usage - .`), + +...`), ); expect(actual).toEqual("## What?\n\nThis is my project.\nIt is good."); @@ -129,7 +133,8 @@ It is good. > See here. ## Usage - .`), + +...`), ); expect(actual).toEqual( @@ -152,7 +157,8 @@ This is my project. It is good. ## Usage - .`), + +...`), ); expect(actual).toEqual("## What?\n\nThis is my project.\nIt is good."); @@ -175,7 +181,57 @@ It is good. > See here. ## Usage - .`), + +...`), + ); + + expect(actual).toEqual( + "## What?\n\nThis is my project.\nIt is good.\n\n> See here.", + ); + }); + + it("returns existing content before a non-Usage h2 when the Usage h2 does not exist", async () => { + const actual = await readReadmeExplainer(() => + Promise.resolve(` + <a href="http://npmjs.com/package/create-typescript-app"><img alt="📦 npm version" src="https://img.shields.io/npm/v/create-typescript-app?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="Project logo: the TypeScript blue square with rounded corners, but a plus sign instead of 'TS'" height="128" src="./docs/create-typescript-app.png" width="128"> + +## What? + +This is my project. +It is good. + +> See here. + +## Contributing + +...`), + ); + + expect(actual).toEqual( + "## What?\n\nThis is my project.\nIt is good.\n\n> See here.", + ); + }); + + it("returns existing content until the end of the file when no subsequent h2 exists", async () => { + const actual = await readReadmeExplainer(() => + Promise.resolve(` + <a href="http://npmjs.com/package/create-typescript-app"><img alt="📦 npm version" src="https://img.shields.io/npm/v/create-typescript-app?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="Project logo: the TypeScript blue square with rounded corners, but a plus sign instead of 'TS'" height="128" src="./docs/create-typescript-app.png" width="128"> + +## What? + +This is my project. +It is good. + +> See here. +`), ); expect(actual).toEqual( diff --git a/src/options/readReadmeExplainer.ts b/src/options/readReadmeExplainer.ts index aff6a33da..5f36fe0d5 100644 --- a/src/options/readReadmeExplainer.ts +++ b/src/options/readReadmeExplainer.ts @@ -1,24 +1,24 @@ +const lastTagMatchers = [`">`, "/p>", "/>"]; + export async function readReadmeExplainer(getReadme: () => Promise<string>) { const readme = await getReadme(); - const indexOfUsageH2 = /## Usage/.exec(readme)?.index ?? readme.indexOf("##"); - if (indexOfUsageH2 === -1) { - return undefined; - } - - const beforeUsageH2 = readme.slice(0, indexOfUsageH2); - - const [indexOfLastTag, lastTagMatcher] = lastLastIndexOf(beforeUsageH2, [ - `">`, - "/p>", - "/>", - ]); + const indexOfFirstH2 = readme.indexOf("##"); + const indexOfUsageH2 = /## Usage/.exec(readme)?.index; + const beforeH2s = readme.slice(0, indexOfUsageH2 ?? indexOfFirstH2); + const [indexOfLastTag, lastTagMatcher] = lastLastIndexOf( + beforeH2s, + lastTagMatchers, + ); if (!lastTagMatcher) { return undefined; } + const endingIndex = + indexOfUsageH2 ?? /## (?:Contrib|Develop)/.exec(readme)?.index; + return readme - .slice(indexOfLastTag + lastTagMatcher.length, indexOfUsageH2) + .slice(indexOfLastTag + lastTagMatcher.length, endingIndex) .trim(); }