From c3f10b51754c7d695a759f50c362779b20716ab7 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sun, 8 Nov 2026 10:52:48 +0100 Subject: [PATCH] Copy the README tagline to the npm description at publish; name Jira and Confluence in it --- README.md | 8 +++++-- ci.sh | 1 + package.json | 2 +- publish.sh | 2 ++ release-tooling/tagline.mjs | 12 +++++++++++ release-tooling/tagline.test.mjs | 31 +++++++++++++++++++++++++++ release-tooling/write-description.mjs | 12 +++++++++++ 7 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 release-tooling/tagline.mjs create mode 100644 release-tooling/tagline.test.mjs create mode 100644 release-tooling/write-description.mjs diff --git a/README.md b/README.md index 64a4165..84347a7 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,10 @@ [![npm](https://img.shields.io/npm/v/@larvit/adf-codec)](https://www.npmjs.com/package/@larvit/adf-codec) [![CI](https://github.com/larvit/adf-codec/actions/workflows/ci.yml/badge.svg)](https://github.com/larvit/adf-codec/actions/workflows/ci.yml) -Convert between ADF (Atlassian Document Format) and markdown or HTML. For application developers -whose app shows, edits, exports or generates the rich text Atlassian products store as ADF. +Convert between ADF (Atlassian Document Format, the rich text of Jira and Confluence) and markdown or HTML. + +For application developers whose app shows, edits, exports or generates the rich text Atlassian +products store as ADF. ## Main functionality @@ -784,6 +786,8 @@ All three commands need only Docker, git and bash 5. - `./mutation-tests/run.sh []` mutation-tests the lines changed since ``, `origin/main` by default, in source files git tracks. It needs 6 GB of memory for Docker, and lists the mutants that survived. Survivors don't fail it; a mutant whose test run crashed does. +- The README's first paragraph after the badges is the npm description: `./publish.sh` copies it to + `package.json`. Keep it plain text; `./ci.sh` fails where it isn't. ## Links diff --git a/ci.sh b/ci.sh index 0037fd2..40b3dc2 100755 --- a/ci.sh +++ b/ci.sh @@ -61,6 +61,7 @@ leg "tests ($deno_image)" in_image "$deno_image" deno test --allow-env=PROPERTY_ leg "tests ($bun_image)" in_image "$bun_image" bun test src/ leg "corpus-gathering tests ($node_image)" in_image "$node_image" node --test corpus-gathering/ leg "feature table tests ($node_image)" in_image "$node_image" node --test feature-table/ +leg "release tooling tests ($node_image)" in_image "$node_image" node --test release-tooling/ leg "build ($node_image)" in_image "$node_image" npm run build leg "feature table is current ($node_image)" in_image "$node_image" node feature-table/build.mjs --check diff --git a/package.json b/package.json index 9a0cf50..1eace83 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@larvit/adf-codec", "version": "0.3.0", - "description": "Convert Atlassian Document Format (ADF), the rich text of Jira and Confluence, to and from markdown, with a lossless round-trip, and to HTML. No React, no Atlaskit, no runtime dependencies: runs in Node, Forge, Deno, Bun and browsers.", + "description": "Convert between ADF (Atlassian Document Format, the rich text of Jira and Confluence) and markdown or HTML.", "keywords": [ "adf", "adf-to-html", diff --git a/publish.sh b/publish.sh index f1e9022..ee45a49 100755 --- a/publish.sh +++ b/publish.sh @@ -35,6 +35,8 @@ if [ -z "$published" ]; then : "${NPM_TOKEN:?the publish needs NPM_TOKEN}" leg "install ($node_image)" in_image "$node_image" npm ci leg "build ($node_image)" in_image "$node_image" npm run build + # npmjs shows the README's tagline, which package.json's description is overwritten with here. + leg "copy the README tagline to the description ($node_image)" in_image "$node_image" node release-tooling/write-description.mjs leg "publish $name@$version ($node_image)" \ docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp -e NPM_TOKEN -v "$PWD:/app" -w /app --entrypoint sh "$node_image" -c \ 'printf "//registry.npmjs.org/:_authToken=%s\n" "$NPM_TOKEN" > "$HOME/.npmrc" && npm publish --access public' diff --git a/release-tooling/tagline.mjs b/release-tooling/tagline.mjs new file mode 100644 index 0000000..79f3e18 --- /dev/null +++ b/release-tooling/tagline.mjs @@ -0,0 +1,12 @@ +const markup = /[`[\]*_<>]/ + +// The README's first paragraph after the title and the badges. +export function readmeTagline(readme) { + const lines = readme.split('\n').filter((line) => !line.startsWith('# ') && !line.startsWith('[![')) + const start = lines.findIndex((line) => line.trim() !== '') + if (start === -1 || lines[start].startsWith('#')) return { error: 'README.md has no tagline paragraph before its first section', ok: false } + const end = lines.findIndex((line, index) => index > start && line.trim() === '') + const tagline = lines.slice(start, end === -1 ? undefined : end).map((line) => line.trim()).join(' ') + if (markup.test(tagline)) return { error: `the README tagline is plain text, since npm shows it as is: ${tagline}`, ok: false } + return { ok: true, value: tagline } +} diff --git a/release-tooling/tagline.test.mjs b/release-tooling/tagline.test.mjs new file mode 100644 index 0000000..565b208 --- /dev/null +++ b/release-tooling/tagline.test.mjs @@ -0,0 +1,31 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { test } from 'node:test' + +import { readmeTagline } from './tagline.mjs' + +const title = '# @larvit/pkg — a title\n\n[![npm](https://img.example/npm.svg)](https://example.com)\n[![CI](https://img.example/ci.svg)](https://example.com)\n\n' + +test('is the first paragraph after the title and the badges', () => { + assert.deepEqual(readmeTagline(`${title}Convert A to B.\n\nFor people who need it.\n\n## Install\n`), { ok: true, value: 'Convert A to B.' }) +}) + +test('joins a paragraph wrapped over lines with single spaces', () => { + assert.deepEqual(readmeTagline(`${title}Convert A to B,\nand back.\n\nMore.\n`), { ok: true, value: 'Convert A to B, and back.' }) +}) + +test('refuses a README with no paragraph before its first section', () => { + assert.equal(readmeTagline(`${title}## Install\n`).ok, false) +}) + +test('refuses markup, which npm would show as raw characters', () => { + for (const markup of ['`code`', '[link](https://example.com)', '**bold**', '_em_', 'x']) { + assert.equal(readmeTagline(`${title}Convert ${markup}.\n`).ok, false, markup) + } +}) + +test('reads the real README', () => { + const tagline = readmeTagline(readFileSync(new URL('../README.md', import.meta.url), 'utf8')) + assert.ok(tagline.ok, tagline.error) + assert.ok(tagline.value.includes('Jira')) +}) diff --git a/release-tooling/write-description.mjs b/release-tooling/write-description.mjs new file mode 100644 index 0000000..b2065b9 --- /dev/null +++ b/release-tooling/write-description.mjs @@ -0,0 +1,12 @@ +import { readFileSync, writeFileSync } from 'node:fs' + +import { readmeTagline } from './tagline.mjs' + +const packagePath = new URL('../package.json', import.meta.url) +const tagline = readmeTagline(readFileSync(new URL('../README.md', import.meta.url), 'utf8')) +if (!tagline.ok) { + console.error(tagline.error) + process.exit(1) +} +const manifest = JSON.parse(readFileSync(packagePath, 'utf8')) +writeFileSync(packagePath, `${JSON.stringify({ ...manifest, description: tagline.value }, null, 2)}\n`)